ThreadLoacal

53 阅读2分钟

1、对象关系图

image.png

源码

1、ThreadLocal.set

public void set(T value) {
  Thread t = Thread.currentThread();
  ThreadLocalMap map = getMap(t); //1、去当前线程中获取Map
  if (map != null)
    map.set(this, value); //2、ThreadLocal对象作为key 值作为value 放到Map中。
  else
    createMap(t, value);
}

2、ThreadLocal.get

public T get() {
  Thread t = Thread.currentThread();
  ThreadLocalMap map = getMap(t); //1、当前线程中获取Map
  if (map != null) {
    ThreadLocalMap.Entry e = map.getEntry(this); //2、用ThreadLocal对象作为key取值
    if (e != null) {
      @SuppressWarnings("unchecked")
      T result = (T)e.value;//3、返回值
      return result;
    }
  }
  return setInitialValue();
}

3、ThreadLocal原理总结

  1. 每个Thread维护着一个ThreadLocalMap的引用
  2. ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
  3. 调用ThreadLocal的set()方法时,实际上就是往当前线程的ThreadLocalMap内设置值,key是ThreadLocal对象,值是传递进来的对象
  4. 调用ThreadLocal的get()方法时,实际上就是往当前线程的ThreadLocalMap获取值,key是ThreadLocal对象
  5. ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value

正因为这个原理,所以ThreadLocal能够实现“数据隔离”,获取当前线程的局部变量值,不受其他线程影响~

4、QA

内存泄露

虽然ThreadLocalMap中的key是弱引用,当不存在外部强引用的时候,就会自动被回收,但是Entry中的value依然是强引用。这个value的引用链条如下:

image.png

可以看到,只有当Thread被回收时,这个value才有被回收的机会,否则,只要线程不退出,value总是会存在一个强引用。但是,要求每个Thread都会退出,是一个极其苛刻的要求,对于线程池来说,大部分线程会一直存在在系统的整个生命周期内,那样的话,就会造成value对象出现泄漏的可能。处理的方法是,在ThreadLocalMap进行set(),get(),remove()的时候,都会进行清理:

5、参考

juejin.cn/post/684490…

juejin.cn/post/695933…

juejin.cn/post/693280…