ThreadLocal总结一下

570 阅读2分钟

介绍

ThreadLocal提供线程局部变量。跟普通变量不同,每个访问它的线程都有线程自己的,独立初始化的变量副本。ThreadLocal实例通常是作为类的私有静态字段,把类的状态(例如用户名,用户ID或者交易ID)与线程进行关联。

只要线程是存活的并且ThreadLocal实例是可访问的,则每个线程都对其线程局部变量的副本持有隐式引用。线程消失后,其线程本地实例的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用)。

实现原理

在Thread类里面有一个threadLocals的变量,类型是ThreadLocal.ThreadLocalMap,当前线程所有ThreadLocal变量都保存在该map中,key是用户定义的ThreadLocal变量本身,值是ThreadLocal包含的数值。

threadLocals变量在线程中的默认值是null,线程退出时会把threadLocals变量赋值为null。

ThreadLocal操作

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    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();
    }

ThreadLocal内存泄漏

  1. 使用线程池,线程一直在存活,有多个ThreadLocal实例,有些ThreadLocal变量已经释放掉,有些没有,释放掉的ThreadLocal变量对应的threadLocals指向的ThreadLocalMap的key因为是弱引用而被回收,对应的value值一直没有被回收导致。需要调用remove方法。
  2. 使用线程池,线程一直在存活,有一个ThreadLocal实例,不同系统的多个用户,每个用户设置自己的用户名,如果一个用户没有设置,可能会导致该用户使用到其他用户的用户名。需要调用remove。

延伸

ThreadLocal.ThreadLocalMap inheritableThreadLocals变量

InheritableThreadLocal继承自ThreadLocal,其提供了一个特性,就是让子线程可以访问在父线程中设置的本地变量。

实现原理是在线程初始化的init里面判断父线程的inheritableThreadLocals不为null,则用该Map创建一个新的Map副本。