1、ThreadLocal线程的局部变量。既然是线程局部变量,那么理所当然就应该存储在自己的线程对象中,我们可以从 Thread 的源码中找到线程局部变量存储的地方:
我们可以看到线程局部变量是存储在Thread对象的 threadLocals 属性中,而 threadLocals 属性是一个 ThreadLocal.ThreadLocalMap 对象。
2、ThreadLocalMap是什么呢?
可以看到他是ThreadLocal的一个静态内部类,内部是一个自己实现的以ThreadLocal为key值的map,使用了弱引用的方式。方便内存的回收。
从构造函数中可以看出,他是一个 private Entry[] table数组,通过firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1)实现key到value的映射,初始化大小为16。
threadLocalHashCode 是一个原子变量每次增加0x61c88647实现,然后 & (INITIAL_CAPACITY - 1) 取得在数组 private Entry[] table 中的索引。
3、ThreadLocal初始化
首先在当前线程中获取threadLoals,如果为null,则进行初始化工作。
4、内存的回收
ThreadLocal 涉及到的两个层面的内存自动回收
当线程死亡时,那么所有的保存在的线程局部变量就会被回收,其实这里是指线程Thread对象中的 ThreadLocal.ThreadLocalMap threadLocals 会被回收,这是显然的。
如果线程可以活很长的时间,并且该线程保存的线程局部变量有很多(也就是 Entry 对象很多),那么就涉及到在线程的生命期内如何回收 ThreadLocalMap 的内存了,不然的话,Entry对象越多,那么ThreadLocalMap 就会越来越大,占用的内存就会越来越多,所以对于已经不需要了的线程局部变量,就应该清理掉其对应的Entry对象。使用的方式是,Entry对象的key是WeakReference 的包装,当ThreadLocalMap 的 private Entry[] table,已经被占用达到了三分之二时 threshold = 2/3(也就是线程拥有的局部变量超过了10个) ,就会尝试回收 Entry 对象,我们可以看到 ThreadLocalMap.set方法中有下面的代码:
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
5、 ThreadLocal常用的接口:
1)需要制定初始值时,可以覆盖protected T initialValue()方法;
2)public T get();
3)public void set(T value);
4)public void remove();
6、总结
1)一个线程中的所有的局部变量其实存储在该线程自己的同一个map属性中;
2)线程死亡时,线程局部变量会自动回收内存;
3)线程局部变量时通过一个 Entry 保存在map中,该Entry 的key是一个 WeakReference包装的ThreadLocal, value为线程局部变量;
key 到 value 的映射是通过:ThreadLocal.threadLocalHashCode & (INITIAL_CAPACITY - 1) 来完成的;
4)当线程拥有的局部变量超过了容量的2/3(没有扩大容量时是10个),会涉及到ThreadLocalMap中Entry的回收;
参考文章:https://www.cnblogs.com/digdeep/p/4510875.html