美文网首页
ThreadLocal(二)

ThreadLocal(二)

作者: 50e383299c68 | 来源:发表于2018-01-24 20:27 被阅读0次
    1. 实现原理
      ThreadLocal可以看做是一个容器,容器里面存放着属于当前线程的变量。
      ThreadLocal类提供了四个接口方法,这也是用户操作ThreadLocal类的基本方法:
      (1) void set(Object value)设置当前线程的线程局部变量的值。
      (2) public Object get()该方法返回当前线程所对应的线程局部变量。
      (3) public void remove()将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK 5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。防止内存泄漏。例如在线程池中,可能该线程一直存在,线程中的ThreadLocal对象也会一直存在。
      (4) protected Object initialValue()返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次,ThreadLocal中的缺省实现直接返回一个null。

    ThreadLocal内部是如何为每一个线程维护变量副本的呢?其实在Thread类中有一个成员变量ThreadLocal.ThreadLocalMap threadLocals 存储线程实例的变量副本。ThreadLocalMap是ThreadLocal类的静态内部类。这个变量threadLocals的维护都是由ThreadLocal操作。
    但是ThreadLocalMap不是map类型,而是数组类型,通过ThreadLocal对象的hashcode计算对应的数组索引,数组存储的value是线程中变量的副本,每个线程可能存在多个ThreadLocal对象。

    public T get() {
        Thread t = Thread.currentThread();//当前线程
        ThreadLocalMap map = getMap(t);//获取当前线程对应的ThreadLocalMap
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);//获取对应ThreadLocal的变量值
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();//若当前线程还未创建ThreadLocalMap,则返回调用此方法并在其中调用createMap方法进行创建并返回初始值。
    } 
    
    //设置变量的值
    public void set(T value) {
       Thread t = Thread.currentThread();
       ThreadLocalMap map = getMap(t);
       if (map != null)
           map.set(this, value);
       else
           createMap(t, value);
    }
    
    /**
    为当前线程创建一个ThreadLocalMap的threadlocals,并将第一个值存入到当前map中
    @param t the current thread
    @param firstValue value for the initial entry of the map
    */
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }  
    
    //删除当前线程中ThreadLocalMap对应的ThreadLocal
    public void remove() {
           ThreadLocalMap m = getMap(Thread.currentThread());
           if (m != null)
               m.remove(this);
    }
    

    上述是在ThreadLocal类中的几个主要的方法,他们的核心都是对其内部类ThreadLocalMap进行操作,下面看一下该类的源代码:

    static class ThreadLocalMap {
      //map中的每个节点Entry,其键key是ThreadLocal并且还是弱引用,这也导致了后续会产生内存泄漏问题的原因。
     static class Entry extends WeakReference<ThreadLocal<?>> {
               Object value;
               Entry(ThreadLocal<?> k, Object v) {
                   super(k);
                   value = v;
       }
        /**
         * 初始化容量为16,以为对其扩充也必须是2的指数 
         */
        private static final int INITIAL_CAPACITY = 16;
        /**
         * 真正用于存储线程的每个ThreadLocal的数组,将ThreadLocal和其对应的值包装为一个Entry。
         */
        private Entry[] table;
    }
    
    1. 内存泄漏问题
      线程执行完操作后如果只是将ThreadLocal对象置为null。例如 ThreadLocal<Integer> seqNum = null。会造成内存泄漏。原因如下:ThreadLocal是个容器,容器里面的对象没有处理。容器里面的对象的引用存储在ThreadLocalMap中。如果线程操作完后,还一直存在在线程池里面,这个线程的ThreadLocalMap也一直存在,ThreadLocalMap里面的变量副本的引用也一直存在。这样导致变量副本也一直存在,造成内存泄漏。
      解决方法:ThreadLocal对象使用完之后调用ThreadLocal.remove()。ThreadLocal对象的remove()方法会把ThreadLocalMap中对应的变量副本的引用删掉。

    相关文章

      网友评论

          本文标题:ThreadLocal(二)

          本文链接:https://www.haomeiwen.com/subject/pqcpaxtx.html