ThreadLocal使用与原理

46 阅读2分钟

ThreadLocal 使用场景

  • 每个线程都需要一个独享的对象(一般用于工具类) 这样做的目的是防止创建过多对象

  • 用于多个线程的都需要保存的全局变量

    • 解决多层方法调用直接的方法传递问题 如果 设置语言环境 这样就不需要把参数从 controller->service->dao 层层传递下来
    • 例如 日志初始化日志 tag需要在任意输出日志时需要缓存对应信息

ThreadLocal原理

Thread ThreadLocal ThreadLocalMap 关系

未命名文件.png

ThreadLocal重要方法

  • void initialValue() 延迟加载 初始赋值, 客户端如果没有使用就get 会调用initialValue
  • T get()
   public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        //
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }
  • void set()
  • void remove()

ThreadLocal使用需要注意点

使用ThreadLocal 可能导致导致内存泄漏问题

ThreadLocal 可能导致导致内存泄漏问题原因

ThreadLocalMap设计 key 为ThreadLocal且会弱引用 value 是强引用对象

如果 线程不终止 则Thread-> ThreadLocalMap-> Entry-> value 生命周期等同线程的生命周期

Entry的key 是弱引用可以被GC回收,因value为强引用导致key为null时 无法被使用则发生内存泄漏 Thead->ThreadLocalMap->Entry( (key弱引用,value))->value

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

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

ThreadLocalMap为防止内存泄漏做出的设计

set ,remove,rehash 会扫描key为null 的entry 并会把对应value设置为null

ThreadLocal使用NPE问题

ThreadLocal 赋值后,如果新开线程之间get 则会导致NPE问题 主要get方法对应基本类型拆箱问题

ThreadLocal,InheritableThreadLocal,TransmittableThreadLocal

ThreadLocal缺陷

如果新开一个子线程 则无法传递给子线程,可以使用解决 InheritableThreadLocal

InheritableThreadLocal缺陷

如果存在线程复用情况,InheritableThreadLocal则会出现问题需要使用TransmittableThreadLocal

参考1

参考2