《Java高并发程序设计》 --ThreadLocal
public class ThreadLocalDemo { static ThreadLocal<Integer> local = new ThreadLocal<Integer>(){ @Override protected Integer initialValue() { return 0; } }; public static void main(String[] args) { Thread[] threads = new Thread[5]; for (int i = 0; i < 5; i++) { threads[i] = new Thread(()->{ Integer num = local.get(); local.set(num+ 5); System.out.println(Thread.currentThread().getName()+ "-" + num); }); } for (int i = 0; i < 5; i++) { threads[i].start(); } } }
代码输出:
Thread-0-0 Thread-3-0 Thread-4-0 Thread-2-0 Thread-1-0
解释:
各个线程获取的值不会受到其他线程修改值的影响
原理大致如图:
对流程的说明:
对于新开启的一个线程Thread,内部有一个ThreadLocalMap对象(类似于hash map)存储供当前线程使用的自定义的Object value。
该map的key为ThreadLocal实例,值为用户自定义使用的值。
下面就解析一下ThreadLocal的set方法
这个方法分为两步走:
- 创建新的ThreadLocalMap
- 将当前的ThreadLocal放入到已有的ThreadLocalMap中
看第一步:
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }
就是ThreadLocalMap的构造函数啦, 给table赋值一个 初始化大小的Entry数组,再根据threadLocal的hashcode和长度进行&比较获取table中的下标,然后插入新的Entry值。
看第二步啦:
这有三个if,一个for:
for: 遍历ThreadLocalMap中的Entry数组,找到不为空的进行判断
- 如果是同一个坑的key,直接覆盖value
- 如果k为空,也就是threadLocal为空但是value还存在的情况,
注释:当一个ThreadLocal失去强引用,生命周期只能存活到下次gc前,此时ThreadLocalMap中就会出现key为null的Entry,当前线程无法结束,这些key为null的Entry的value就会一直存在一条强引用链,造成内存泄露。
for 循环遍历结束后,将在对应的table位置新建一个entry存放新值。
并处理那些标记为需要清除的slot entry ,并进行rehash