ThreadLocal

ThreadLocal简介:

​ ThreadLocal叫做线程局部变量,其中填充的变量属于当前线程,对于其他的线程是隔离的,属于线程私有。ThreadLocal会为每个线程中都创建一个工作副本,而每个线程都只可以访问自己内部的副本变量。

ThreadLocal特点:

  1. ​ ThreadLocal提供了线程本地的实例副本,该副本只能由当前的Thread使用
  2. ​ 因为每个Thread都有自己的实例副本,且其他Thread不可以访问,也就不会存在共享的问题
  3. ​ 当一个线程结束时,他说使用的所有 ThreadLocal 相对的实例副本都可被回收
  4. ​ ThreadLocalMap其实是Thread线程的一个属性值,而ThreadLocal是维护ThreadLocalMap

图片说明

ThreadLocal与Synchronize的区别

  • synchronize侧重于线程间的数据共享,而ThreadLocal则更偏向与线程间的数据隔离
  • synchronize是利用锁的机制,使变量或者方法在某一时刻只能被一个线程访问,而ThreadLocal为每个线程都提供了一个副本,使得每个线程在同一时间访问到的并不是同一个对象,隔离了多个线程间的数据共享

ThreadLocal方法分析

  1. set方法
    public void set(T value) {
        //1、首先获取当前线程
            Thread t = Thread.currentThread();
        //2、获取线程中的ThreadLocalMap属性,如果ThreadLocalMap不为空,则直接
        //保存要保存的变量值,否则就创建ThreadLocalMap,并赋值
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    //创建ThreadLocalMap
      void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }

    其中ThreadLocalMap是ThreadLocal中的静态内部类,而它的内部中的构成Entry继承了WeakReference弱引用,用来保存数据。在Entry内部使用ThreadLocal作为key,使用我们设置的value作为value。

  2. get方法
    public T get() {
        //获取当前线程
            Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                //从当前线程的ThreadLocalMap中获取Entry对象
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
        //如果没有就返回null
            return setInitialValue();
        }   
    
     private T setInitialValue() {
         //将value值设置为null
            T value = initialValue();
         //获取当前线程的对象
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                //如果map不为null,就给当前map设置值
                map.set(this, value);
            else
                //否则就创建map
                createMap(t, value);
            return value;
        }
    
    ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
  1. remove方法
     public void remove() {
             ThreadLocalMap m = getMap(Thread.currentThread());
             if (m != null)
                 m.remove(this);
         }

remove方法直接将线程ThreadLocal中的ThreadLocalMap删除,如果不删除,很有可能会发生内存泄露。

ThreadLocal为什么容易发生内存泄露

​ ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,弱引用的特点是,如果这个对象只存在弱引用,那么在下一次垃圾回收的时候必然会被清理掉。如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候会被清理掉的,这样一来 ThreadLocalMap中使用这个 ThreadLocal 的 key 也会被清理掉。但是,value 是强引用,不会被清理,这样一来就会出现 key 为 null 的 value。