前言
- ThreadLocal 是一种无同步的线程安全实现
- 体现了
Thread-Specific Storage
模式:即使只有一个入口,内部也会为每个线程分配特有的存储空间,线程间没有共享资源,实现了无锁线程安全 - 本文将总结
ThreadLocal
的用法与实现细节,希望能帮上忙
1. 用法
ThreadLocal
的用法很简单,ThreadLocal
提供了下列的public与protected方法:
现在我们查看ThreadLocal
中与上述几个方法有关的代码,简化代码如下:
// ThreadLocal.java
// ThreadLocal构造方法里什么都没做
public ThreadLocal() {
// do nothing
}
// 定义ThreadLocal变量的初始值
protected T initialValue() {
// 默认的初始值为null
return null;
}
// 内部方法:用于设置当前线程里ThreadLocal变量初始值
private T setInitialValue() {
T value = initialValue();
// 其实ThreadLocal的源码并不是直接调用set(),但源码中这部分代码
// 就相当于调用set()方法,这是为了防止子类重写set()造成异常
set(value);
return value;
}
// 获取当前线程中ThreadLocal变量的值
public T get() {
Thread t = Thread.currentThread();
// ThreadLocalMap是什么?稍后介绍
ThreadLocalMap map = getMap(t);
if (map != null) {
// 存在匹配的Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
// 变量的值不为null,返回
T result = (T)e.value;
return result;
}
}
// 获取的值为空,设置变量的初始值并返回
return setInitialValue();
}
// 设置当前线程中ThreadLocal变量的值
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
// ThreadLocalMap懒初始化,直到设置值的时候才创建
createMap(t, value);
}
// 移除当前线程中ThreadLocal变量的值
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap
存储在Thread的属性中,简化代码如下:
// Thread.java
ThreadLocal.ThreadLocalMap threadLocals = null;
// 线程退出之前,会置空threadLocals变量,以便随后GC
private void exit() {
// ...
threadLocals = null;
// ...
}
分析代码,可以总结出方法的用法:
- 1.
get()
获取当前线程中ThreadLocal
变量的值- 不同线程获取的值互不干扰
- 如果取值为null,则调用
initialValue()
设置初始值
-
-
set()
设置当前线程ThreadLocal
变量的值
- 不同线程设置的值互不干扰,不会相互覆盖
-
-
-
remove()
移除当前线程之前设置在ThreadLocal
变量上的值
- 如果在当前线程下次调用
get()
之前,还没有调用set()
设置新值,则依旧会调用setInitialValue()
重新设置初始值。
-
-
-
initialValue()
子类重写此方法可以定义ThreadLocal
变量的初始值
- 默认的初始值为null
-
总结一下ThreadLocal
的生命周期,如下图所示:
2. 例子
我们看看android.os.Looper.java 中是如何使用ThreadLocal
,简化代码如下:
// /frameworks/base/core/java/android/os/Looper.java
public class Looper {
// ...
// 静态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");
}
// 设置ThreadLocal变量的值
sThreadLocal.set(new Looper(quitAllowed));
}
public static Looper myLooper() {
// 获取ThreadLocal变量的值
return sThreadLocal.get();
}
public static void prepare() {
prepare(true);
}
// ...
}
-
ThreadLocal
被声明为static final变量,泛型参数为Looper
,表示ThreadLocal
变量接受Looper
类型的值 -
prepare()
中调用ThreadLocal#set()
设置当前线程的Looper
-
myLooper()
中调用ThreadLocal#get()
获取当前线程的Looper
我们可以画出Looper
中访问ThreadLocal
的Timethreads图,如下图所示,不同线程独占一个Looper
变量,线程间不存在共享资源。可以看到ThreadLocal
实现了无锁线程安全,避免了加解锁造成的上下文切换,体现了空间换时间的思想。
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修饰,可以保证变量在类初始化时创建,所有类实例可以共享同一个静态变量。注意到了吗?在文章开头的Looper.java源码中,
ThreadLocal
变量就是使用static
修饰的
4. 使用场景
-
以空间换时间实现无锁线程安全
ThreadLocal
相对于Synchronized等互斥锁避免了上下文切换损耗,有助于提高吞吐量 -
线程级别的单例模式
一般的单例对象是对整个进程可见的,假如这个对象不是线程安全的(比如SimpleDateFormat),就可以很方便的使用
ThreadLocal
实现线程级别的单例,保证线程安全 -
共享参数
如果一个模块有非常多地方需要使用同一个变量,相比于在每个方法中重复传递同一个参数,使用
ThreadLocal
作为一个全局变量也许是另一种选择方式。
看到这里,相信你已经掌握了ThreadLocal
的用法,下一篇文章将深入ThreadLocal
的核心,探讨数据结构ThreadLocalMap
的实现细节,欢迎关注彭旭锐的简书!
推荐阅读
- Android | InputManagerService 与输入事件采集
- Android | 再按一次返回键退出
- 设计模式 | 静态代理与动态代理
- Cocos Creator | 封装音频池工具
- 笔记 | 使用 Markdown 高效率编写文档
- 笔记 | AndroidStudio 极速编译
- 笔记 | 使用 Keytool 管理密钥和证书
参考
- ThreadLocal.java — Josh Bloch and Doug Lea
- 《深入理解Java虚拟机 — JVM高级特性与最佳实践》 周志明 著
- 《Java并发编程的艺术》 方腾飞 魏鹏 程晓明 著
- 《数据结构与算法分析 — Java语言描述》 [美]Mark Allen Weiss 著
- 《阿里巴巴Java开发手册》 杨冠宝 编著
网友评论