ThreadLocal 源码解读
一、引入
public class Thread implements Runnable {
/* 前面略 */
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/* 后面略 */
}
首先我们看到的是 Thread 中有一个属性 threadLocals,它的类型是 ThreadLocalMap,封装类型是 default(表示它只能在包内可见),jdk 是这么介绍它的:与此线程有关的 ThreadLocal 值,该映射由 ThreadLocal 类维护。 啥意思呢?那就来看看 ThreadLocalMap 是啥玩意!
public class ThreadLocal<T> {
/* 前面略 */
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/* 后面略 */
}
}
从类定义上可以看出 ThreadLocal 是支持泛型的,而 ThreadLocalMap 是 ThreadLocal 的一个内部类,封装类型也是 default(表示只能在包内可见),jdk 是这么介绍它的:ThreadLocalMap 是自定义的哈希映射,仅适用于维护线程局部值。并且为了存储容量可控,不至于内存泄漏,哈希表条目使用弱引用作为键(弱引用的对象的生命周期直到下一次垃圾回收之前被回收),ThreadLocalMap 使用静态内部类 Entry(可以类比 Map 中的 entry)来存储实际的 key 和 value。
从上面这些介绍,我们可以大致想到,ThreadLocal 是一个与线程相关的类,用来存储维护线程局部值。
二、set(T value) 方法解读
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以看到 set(T value) 方法做的事情很简单,就是维护 Thread 的 threadLocals 属性,如果该属性不存在的话,就以当前 ThreadLocal 实例为 key 创建一个;该属性存在的话,则直接赋值。
三、get() 方法解读
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
get() 的方法同样很简单,就是从 Thread 的 threadLocals 属性获取值,如果获取不到,则把 initialValue() 的值赋值给线程的 threadLocals 属性并返回。initialValue() 方法是一个 protected 类型的方法,默认返回 null,我们可以在创建 ThreadLocal 的时候重写它,表示所有线程的默认值。
// java8 的方式
ThreadLocal<Boolean> threadLocal1 = ThreadLocal.withInitial(() -> false);
//
ThreadLocal<Boolean> threadLocal2 = new ThreadLocal<Boolean>() {
@Override
protected Boolean initialValue() {
return false;
}
};
四、总结
- ThreadLocal 用于存储维护线程的局部值。
- 和 ThreadLocal 类似的还有一个叫 InheritableThreadLocal, InheritableThreadLocal 继承自 ThreadLocal,用于父子线程间共享共同的值,父线程中设置的值,子线程中可以访问到。
- 上面看 ThreadLocalMap 的时候,我们知道 key 是弱引用,gc 的时候 key 会被回收,但是 value 和 ThreadLocalMap 的引用不会被回收,如果这种情况的 Thread 很多,而且一直没有执行完,就可能会出现内存泄漏,因此使用完 ThreadLocal 的时候尽量调用 ThreadLocal 的 remove() 方法。
- 当使用线程池的时候,在调用 ThreadLocal 的 set 方法后,却没有调用 remove 的方法,如果同一个线程再去调用 get 方法可能拿到的值并不是当时 set 进去的(因为线程池的线程是复用的),可能导致程序数据异常之类的,因此使用完 ThreadLocal 的时候尽量调用 ThreadLocal 的 remove() 方法。
相关文章
- 基于JSP的网上购物系统的设计与实现(论文+源码)_kaic
- BERT源码实现与解读(Pytorch)
- Nvidia Jetson 开发套件,视频解码的相关源码解读:videoSource.h、videoSource.cpp
- 用VS2010编译python2.7的源码
- 线程本地变量ThreadLocal源码解读
- 前端Backbone源码解读(二)
- SpringBoot 源码解析 (二)----- Spring Boot精髓:启动流程源码分析
- Spring源码解析 - springMVC核心代码
- 第127课: Spark Streaming源码经典解读系列之二:Spark Streaming生成RDD
- 第132课: Spark Streaming源码经典解读系列之七:Executro容错工作内幕源码解密
- 第14课:Spark Streaming源码解读之State管理之updateStateByKey和mapWithState解密
- 第36课:Kafka源码解读SocketServer下的Acceptor、Processor、Handler
- 第15课:spark streaming源码解读之No Receives彻底思考
- 《Spark商业案例与性能调优实战100课》第25课:Spark Hash Shuffle源码解读与剖析
- 第25课 Spark Hash Shuffle源码解读与剖析
- MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)
- Spring Security 源码解读:权限控制
- canal 源码解析系列-CanalServerWithEmbedded解读
- Fresco源码解析 - DataSource怎样存储数据
- Yarn源码分析之MapReduce作业中任务Task调度整体流程(一)