ThreadLocal

57 阅读2分钟

threadLocal的使用

private static final ThreadLocal<Objcet> threadLocal = new ThreadLocal();

public void func(Object o) {
    threadLocal.set(o);
    System.out.println(o);
    threadLocal.remove();
}

ThreadLocal的存储结构

image.png 在执行set方法存储时,值并没有保存在threadLocal对象中,而是保存在了Thread对象中的threadLocals,以threadLocal对象为key存储进Map中;

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

设置了KEY(ThreadLocal)为弱引用对象,为什么要设置为弱引用,强引用不行吗?

ThreadLocal的弱引用问题

众所周知java对象按引用类型可以分为四种:

  1. 强引用 A a=new A(); 此时引用a强引用对象A;不会被GC
  2. 软引用(softReference)在内存不够时引用对象会被GC;
  3. 弱引用(weakReference)每次GC都会被回收
  4. 虚引用(PhantomReference)每次GC都会被回收

如果设置ThreadLocal为强引用的话,在业务执行结束之后,执行remove方法,此时value=null,但此时threadLocalMap还持有一个threadLocal的强引用,所以不会被GC;

如果是弱引用到的话,value=null, threadLocal为弱引用,弱引用会被gc清理掉;

InheritableThreadLocal

问题: 子线程无法读取到父线程的threadLocal

InheritableThreadLocal在先线程初始化的时候会复制一份父线程的threadLocal对象内容到inheritableThreadLocals变量中;

TranmittableThreadLoca

问题:由于InheritableThreadLocal只会在线程初始化的时候复制一次,如果是线程复用的情况下,如使用线程池,就可能会导致线程本地变量混乱;

阿里开源的这个TranmittableThreadLocal对象,就能完美解决这个问题,他是在线程池启动任务的时候去复制的本地变量,所以不会有问题;

参考