开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 11 天,点击查看活动详情
上一篇文章 # 聊聊 ThreadLocal 类(一) ,我们简单看了下 ThreadLocal 在多线程环境下实现共享变量的方式。那么 ThreadLocal 是如何实现每个线程均拥有共享变量的副本,对共享变量的读取和写入互不影响的呢?本篇文章,我们从源码的角度一起来揭秘吧!
ThreadLocal 的原理
之前我们有分析过 Thread 类的源码,Thread 类中有两个成员变量:
public class Thread implements Runnable {
// 省略部分代码
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
// 省略部分代码
}
它们的类型都是 ThreadLocal.ThreadLocalMap 类型,且二者的初始值都为null。只有当前线程第一次调用 ThreadLocal 的 set() 方法或者 get() 方法时才会实例化这两个变量。接下来,我们来看看 ThreadLocal 的 set() 方法和 get() 方法的实现。
set 方法
先来看看 set 方法的源码
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- 首先会获取当前线程
- 以当前线程为Key,获取 ThreadLocalMap 对象
- 获取的 ThreadLocalMap 对象不为空,设置 value 的值
- 若获取的 ThreadLocalMap 对象为空,则创建 Thread 类中的 threadLocals 变量
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
- 获取 ThreadLocalMap 对象,实际上就是获取 Thread 对象的 threadLocals 成员变量
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
- 获取 ThreadLocalMap 对象为 null 时,就会给 Thread 的成员变量 threadLocals 进行实例化
get 方法
再来看看 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();
}
- 首先也是先获取当前线程
- 以当前线程为Key,获取 ThreadLocalMap 对象
- 如果 ThreadLocalMap 对象不为 null,则返回本地变量对应的值
- 如果 ThreadLocalMap 对象为 null,则初始化 threadLocals 成员变量的值
remove 方法
remove 方法的作用是删除 ThreadLocalMap 中的 value 资源它的源码比较简单:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
- 根据当前线程获取threadLocals成员变量
- 当 threadLocals 成员变量不为空,则移除 value 值
注意: 如果调用线程一直不终止,则本地变量会一直存放在调用线程的threadLocals成员变量中,所以,如果不需要再使用本地变量时,可以通过调用ThreadLocal的remove()方法,将本地变量从当前线程的threadLocals成员变量中删除,以免出现内存溢出的问题。