Java基础系列(九)ThreadLocal

ThreadLocal一般称为线程本地变量,它是一种特殊的线程绑定机制,将变量与线程绑定在一起,为每一个线程维护一个独立的变量副本。通过ThreadLocal可以将对象的可见范围限制在同一个线程内。

set方法

image.png
  • 获取当前线程;
  • 获取线程t持有的属性threadLocals(类型为ThreadLocal的内部类ThreadLocalMap);
  • 如果线程t持有的属性threadLocals存在,则将当前ThreadLocal类对象作为key,value作为值通过set操作完成映射;
  • 如果线程t持有的属性threadLocals不存在,则创建ThreadLocalMap对象并赋值给当前线程的属性threadLocals。

ThreadLocalMap.set操作

image.png
  • 根据key的threadLocalHashCode和表长度减一(全一)按位进行与操作,得到表长度范围内的索引值;
  • 从索引为0处开始寻找数组元素不为null的元素,判断元素的key和需要set的key是否相等,如果相等,则更新对应的value值;
  • 从循环体中可以看到,ThreadLocalMap 解决冲突的方法是 线性探测法(不断加 1),而不是 HashMap的链地址法,nextIndex的操作是索引位置加一小于数组长度减一时,往后加一,如果超过的话从头开始,实际不会索引到最后一个位置,因为扩容机制导致没有索引到最后一个位置时,元素就为null,从而结束循环操作;
  • 最后索引位置i为null,新建元素对象Entry赋值到索引位置i处。
  • 如果数组的大小大于等于阀值threshold,进行rehash操作。

get操作

image.png
  • 获取当前线程;
  • 根据当前线程获取其属性ThreadLocalMap,从ThreadLocalMap对象中根据当前的ThreadLocal对象获取对应真正需要进行线程隔离的对象。
  • 如果获取不到当前线程的属性ThreadLocalMap,则对当前的ThreadLocal对象进行初始化操作,初始化逻辑是创建当前线程的ThreadLocalMap属性。

ThreadLocal内存泄漏的原因总结

  • Thread 持有ThreadLocalMap对象,
  • ThreadlocalMap对象又持有一个Entry数组,Entry数组的元素为Entry对象,
  • Entry是一个把创建的ThreadLoacal作为弱引用对象构建的一个键值对对象,
  • 垃圾回收后,弱引用对象被回收,键为null,
  • 如果线程对象还存活,线程持有的ThreadlocalMap对象也就存活,但是被回收掉的ThreadLoacal对象对应的ThreadlocalMap对象持有的Entry数组的元素对应的value值就成了不可访问的资源,如果value比较大,有存在比较多的这种键值对,就会发生内存泄漏。


    对象引用关系图.png

参考
参考

你可能感兴趣的:(Java基础系列(九)ThreadLocal)