zoukankan      html  css  js  c++  java
  • java基础-ThreadLocal

    来聊一下ThreadLocal的实现原理和它的内存泄漏问题

    首先来看一个官方示例,这里构造了一个ThreadId类,其作用是在每个线程中保存各自的id,此id全局唯一,通过get可以获取id。

     1     private static class ThreadId {
     2         // Atomic integer containing the next thread ID to be assigned
     3         private static final AtomicInteger nextId = new AtomicInteger(1);
     4         // Thread local variable containing each thread's ID
     5         private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>() {
     6             @Override
     7             protected Integer initialValue() {
     8                 return nextId.getAndIncrement();
     9             }
    10         };
    11         // Returns the current thread's unique ID, assigning it if necessary
    12         public static int get() {
    13             return threadId.get();
    14         }
    15     }

    ThreadLocal的构造器是一个空函数,new一个ThreadLocal实例时,唯一的操作就是对threadLocalHashCode的初始化,很明显这是一个hash值,猜测后续会用到map了。

    1 private final int threadLocalHashCode = nextHashCode();

    再来看调用get时,发生了什么

    /**
         * Returns the value in the current thread's copy of this
         * thread-local variable.  If the variable has no value for the
         * current thread, it is first initialized to the value returned
         * by an invocation of the {@link #initialValue} method.
         *
         * @return the current thread's value of this thread-local
         */
        public T get() {
            Thread t = Thread.currentThread();
            //Thread中有一个ThreadLocalMap类型的实例字段,获得当前线程的threadLocalMap,其实就是返回t.threadLocals
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                //以当前ThreadLocal实例为key,获取entry
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    //得到entry中的value
                    T result = (T)e.value;
                    return result;
                }
            }
            //如果当前线程的threadLocalMap为null,或者当前threadLocal还未插入threadLocalMap,则进行相应初始化工作,无非就是初始化map、调用自定义的initialValue方法、将initialValue返回值包装成entry插入map
            return setInitialValue(); 
        }

    看一下map的构造

     1     static class ThreadLocalMap {
     2         ......
     3         static class Entry extends WeakReference<ThreadLocal<?>> {
     4             /** The value associated with this ThreadLocal. */
     5             Object value;
     6 
     7             Entry(ThreadLocal<?> k, Object v) {
     8                 //注意,这里key被包装成了一个WeakReference
     9                 super(k);
    10                 value = v;
    11             }
    12         }
    13         ......
    14         private Entry[] table;
    15         ......
    16         private Entry getEntry(ThreadLocal<?> key) {
    17             //threadLocalHashCode在ThreadLocal初始化时就已生成,全局唯一。这里
    18             int i = key.threadLocalHashCode & (table.length - 1);
    19             Entry e = table[i];
    20             if (e != null && e.get() == key)//e.get()即从WeakReference中取得threadLocal
    21                 return e;
    22             else
    23                 //有意思,一般的hash表都是使用一个链式结构来解决hash冲突,而这里当hash冲突时进行线性探测
    24                 return getEntryAfterMiss(key, i, e);
    25         }
    26         ......
    27     }

    问题来了,为什么entry中的key要包装成WeakReference呢?

    设想,当我们不再需要threadLocal了,以前例来说就是置ThreadId中类变量threadId为null(假设threadId不是final,也没有被其他引用),而Thread类中的threadLocalMap中仍然持有threadId的引用,这就会产生内存泄漏。将threadLocal包装成WeakReference作为key存储,当threadId为null时,该threadLocal在gc时就会被回收,但此时value还在,ThreadLocal会在进行其他操作时删除key为null的value。这确实存在一种内存泄漏隐患,如果之后不在进行ThreadLocal操作,就真释放不掉value了。通常我们需要被声明为ThreadLocal的变量,在运行期间都是不期望它被回收的,所以我们通常会将其声明为static final,如果有回收的需求,也请使用ThreadLocal的remove进行显示释放。

  • 相关阅读:
    vs2008生成的各种文件
    spider_keeper
    scrapyd 参考(https://www.jianshu.com/p/2a189127901a)
    Django之ORM操作
    ES6 快速入门
    vue 数据(data)赋值问题
    vue嵌套路由-query传递参数(三)
    Vue页面加载时,触发某个函数的方法
    vue从一个页面跳转到另一个页面并携带参数
    单选框radio总结(获取值、设置默认选中值、样式)
  • 原文地址:https://www.cnblogs.com/holoyong/p/7264960.html
Copyright © 2011-2022 走看看