「Java 路线」| ThreadLocal
点赞关注,不再迷路,你的支持对我意义重大!
🔥 Hi,我是丑丑。本文 「Java 路线」| 导读 —— 他山之石,可以攻玉 已收录,这里有 Android 进阶成长路线笔记 & 博客,欢迎跟着彭丑丑一起成长。(联系方式在 GitHub)
前言
- ThreadLocal 是解决多线程并发安全的一种解决方案,与 synchronized 有本质区别;
- 在这篇文章里,我将献上 ThreadLocal 的使用方法 & 源码分析。如果能帮上忙,请务必点赞加关注,这真的对我非常重要。
目录
1. 前置知识
这篇文章的内容会涉及以下前置 / 相关知识,贴心的我都帮你准备好了,请享用~
-
并发编程: 「Java 路线」| 并发编程的基础概念
-
哈希表: 【点赞催更】
2. 简介
2.1 定义
ThreadLocal 是一种 Thread-Specific Storage 的多线程编程模式,即:一个入口为多个线程分配不同的副本。由于线程间在某一时刻访问的并非同一个对象,这样就避免了多线程的数据共享,也就避免了并发安全问题。
2.2 使用场景
-
以空间换时间: 相对于Synchronized 等互斥锁避免了上下文切换损耗,有助于提高吞吐量;
-
线程级别的单例: 一般的单例对象是进程单例的,使用 ThreadLocal 可以轻松实现线程级别单例;
-
共享参数: 如果一个模块有非常多地方需要使用同一个变量,相比于在每个方法中重复传递同一个参数,使用 ThreadLocal 作为一个全局变量也许是另一种选择方式。
2.3 编程规约
记得吗?《阿里巴巴Java开发手册》中提到过关于 ThreadLocal 的编程规约,如下所示:
- 5.【强制】SimpleDateFormate 是线程不安全的类,一般不要定义为 static 变量,如果定义为static,必须加锁,或者使用 DateUtils 工具类。正例:
private static final ThreadLocal<DataFormat> df = new ThreadLocal<DateFormat>(){
@Override
protected DateFormat initialValue(){
return new SimpleDateFormat("yyyy-MM-dd");
}
};
说明: 如果是JDK8的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe.
- 15.【参考】(原文过于啰嗦,以下为笔者转述)ThreadLocal 变量建议使用 static 修饰,可以保证变量在类初始化时创建,所有类实例可以共享同一个静态变量。
提示: 在 第 4 节 使用示例 中,ThreadLocal 变量就是使用 static 修饰的。
3. 使用介绍
4. 使用示例
作为一枚 Android 党,我就以熟悉的 Handler 机制中对 ThreadLocal 的应用作为例子:
public class Looper {
1、静态 ThreadLocal 变量,所有类实例共享同一个 ThreadLocal 变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
2、设置 ThreadLocal 变量的值
sThreadLocal.set(new Looper(quitAllowed));
}
public static Looper myLooper() {
3、获取 ThreadLocal 变量的值
return sThreadLocal.get();
}
public static void prepare() {
prepare(true);
}
...
}
要点如下:
- 1、ThreadLocal 被声明为 static final变量,泛型参数为 Looper,表示 ThreadLocal 变量接受 Looper类型的值;
- 2、Looper.prepare() 中调用
ThreadLocal#set()
设置 当前线程 的 Looper; - 3、Looper.myLooper()中调用
ThreadLocal#get()
获取 当前线程 的 Looper。
我们可以画出 Looper 中访问 ThreadLocal 的 Timethreads 图,不同线程访问的是不同的 Looper 对象,线程间不存在共享资源:
5. 源码分析
6. 总结
参考资料
- 《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》 —— 周志明 著
- 《Java并发编程的艺术》 —— 方腾飞,魏鹏,程晓明 著
- 《数据结构与算法分析 — Java语言描述》 [美]Mark Allen Weiss 著
- 《阿里巴巴Java开发手册》 杨冠宝 编著
创作不易,你的「三连」是丑丑最大的动力,我们下次见!