ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变量与线程绑定在一起,为每一个线程维护一个独立的变量副本。通过ThreadLocal可以将对象的可见范围限制在同一个线程内。
set方法
- 获取当前线程;
- 获取线程t持有的属性threadLocals(类型为ThreadLocal的内部类ThreadLocalMap);
- 如果线程t持有的属性threadLocals存在,则将当前ThreadLocal类对象作为key,value作为值通过set操作完成映射;
- 如果线程t持有的属性threadLocals不存在,则创建ThreadLocalMap对象并赋值给当前线程的属性threadLocals。
ThreadLocalMap.set操作
- 根据key的threadLocalHashCode和表长度减一(全一)按位进行与操作,得到表长度范围内的索引值;
- 从索引为0处开始寻找数组元素不为null的元素,判断元素的key和需要set的key是否相等,如果相等,则更新对应的value值;
- 从循环体中可以看到,ThreadLocalMap 解决冲突的方法是 线性探测法(不断加 1),而不是 HashMap的链地址法,nextIndex的操作是索引位置加一小于数组长度减一时,往后加一,如果超过的话从头开始,实际不会索引到最后一个位置,因为扩容机制导致没有索引到最后一个位置时,元素就为null,从而结束循环操作;
- 最后索引位置i为null,新建元素对象Entry赋值到索引位置i处。
- 如果数组的大小大于等于阀值threshold,进行rehash操作。
get操作
- 获取当前线程;
- 根据当前线程获取其属性ThreadLocalMap,从ThreadLocalMap对象中根据当前的ThreadLocal对象获取对应真正需要进行线程隔离的对象。
- 如果获取不到当前线程的属性ThreadLocalMap,则对当前的ThreadLocal对象进行初始化操作,初始化逻辑是创建当前线程的ThreadLocalMap属性。
ThreadLocal内存泄漏的原因总结
- Thread 持有ThreadLocalMap对象,
- ThreadlocalMap对象又持有一个Entry数组,Entry数组的元素为Entry对象,
- Entry是一个把创建的ThreadLoacal作为弱引用对象构建的一个键值对对象,
- 垃圾回收后,弱引用对象被回收,键为null,
-
如果线程对象还存活,线程持有的ThreadlocalMap对象也就存活,但是被回收掉的ThreadLoacal对象对应的ThreadlocalMap对象持有的Entry数组的元素对应的value值就成了不可访问的资源,如果value比较大,有存在比较多的这种键值对,就会发生内存泄漏。