title: ThreadLocal的那些事
ThreadLocal提纲
最近不是不写博客了。而是想搞一个大事情,想体系的写JVM从内存模型到垃圾回收器以及一些我自己推理的阀值设定公式再到类加载。
但是这部分内容其实水很深,怕没写好所以又在看书回顾。
所以有点时间会写一点这种小的工具类的源码,没时间就暂时不写这块了。
ThreadLocal简介
ThreadLocal线程上下文,怎么说了就是每个线程都有一份自己的map.里面存放着当前线程存入的key value。
InheritableThreadLocal是父线程会将数据拷贝一份给子线程的ThreadLocal(这里有两个坑后面说)
ThreadLocal源码解析
ThreadLocal的源码还是非常简单的,看的时候记得配合Thread一起看。下面咱们看下源码
public void set(T value) {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t);//获取线程内部map
if (map != null)
map.set(this, value);
else
createMap(t, value);//为null新建一个map
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;//获取map
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);//新建一个map
}
看是不是很简单其实就是一个线程独享的一份map数据嘛,唯一的这个map用了一个WeakReference关联ThreadLocal,当GC的时候能回收就回收掉数据。
但是也会存在一个坑,因为value的数据不会清空只是key为null了。不过内部也实现了清空value的方法expungeStaleEntry。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//获取map
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//拿值
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
ThreadLocal与InheritableThreadLocal的坑
ThreadLocal的坑
1.内存泄漏,虽然key会清空但是value不会。所以用完了记得remove
2.线程池会线程复用,导致数据脏读。如果没有set方法的时候,用完记得remove。
InheritableThreadLocal是在new Thread的时候会拷贝一份数据到子线程中。
所以很容易推理出下面几个问题
1.线程池的使用,导致数据只能拷贝一次。因为你也就只新建一次Thread,这个东西很多书上把它吹上天。
有时候我是选择拒绝的。
2.父线程修改了数据,子线程是看不到的。因为子线程就是一份复制的数据。
所以如果你真的要用,一定是父线程初始化完数据之后再也不会改变数据的情况下使用。
拷贝的方法在Thread的构造方法里面,有兴趣看一下。这个东西不实用,所以不打算多写。
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
欢迎扫码加入知识星球继续讨论