美文网首页
ThreadLocal探究

ThreadLocal探究

作者: verk | 来源:发表于2019-03-28 12:54 被阅读0次

    什么是ThreadLocal

    ThreadLoacl提供了可以线程封闭的变量存储,提供线程内的局部变量,可以保证在多个线程并发访问/修改同一变量时的线程安全。

    使用方法:

    ThreadLocal<Integer> t = new ThreadLocal();
    t.set(new Integer(0));
    Integer i = t.get(); 
    

    为什么ThreadLocal可以保证线程安全,我们看一下源码。

    
        public void set(T value) {
             //获取当前线程
            Thread t = Thread.currentThread();
             //获取线程内部的Map
            ThreadLocalMap map = getMap(t);
            if (map != null)
                map.set(this, value);
            else
                createMap(t, value);
        }
    
          //ThreadLocalMap.set
         private void set(ThreadLocal<?> key, Object value) {
    
                Entry[] tab = table;
                int len = tab.length;
                //计算哈希和在数组中的下标
                int i = key.threadLocalHashCode & (len-1);
                //从数组下标处开始,向后遍历
                for (Entry e = tab[i];
                     e != null;
                     e = tab[i = nextIndex(i, len)]) {
                    ThreadLocal<?> k = e.get();
    
                    //如果key地址相同,说明是同一个ThreadLocal
                    if (k == key) {
                        e.value = value;
                        return;
                    }
                    //如果entry的key==null,说明弱引用被回收了,但是value依然存在,直接覆盖
                    if (k == null) {
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
    
                //走到这一步,说明没找到之前老的entry,直接新建
                tab[i] = new Entry(key, value);
                int sz = ++size;
                //处理key被回收的entry,避免内存泄漏,并计算是否需要扩容
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }
    
     public T get() {
            //获取当前线程
            Thread t = Thread.currentThread();
            //获取线程内部的Map
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                //以当前的ThreadLocal作为key,查找Entry
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            //否则返回初始化值,创建ThreadLocalMap默认null
            return setInitialValue();
        }
    
       //从Thread中获取ThreadLocalMap
       ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    
      private Entry getEntry(ThreadLocal<?> key) {
                //对key进行hash计算
                int i = key.threadLocalHashCode & (table.length - 1);
                Entry e = table[i];
              
                if (e != null && e.get() == key)
                    return e;
                else
                    return getEntryAfterMiss(key, i, e);
            }
        
    //扩容方法,扩容为之前容量的2倍
            private void resize() {
                Entry[] oldTab = table;
                int oldLen = oldTab.length;
                int newLen = oldLen * 2;
                Entry[] newTab = new Entry[newLen];
                int count = 0;
    
                for (int j = 0; j < oldLen; ++j) {
                    Entry e = oldTab[j];
                    if (e != null) {
                        ThreadLocal<?> k = e.get();
                        if (k == null) {
                            e.value = null; // Help the GC
                        } else {
                            int h = k.threadLocalHashCode & (newLen - 1);
                            while (newTab[h] != null)
                                h = nextIndex(h, newLen);
                            newTab[h] = e;
                            count++;
                        }
                    }
                }
    
                setThreshold(newLen);
                size = count;
                table = newTab;
            }
      private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
                Entry[] tab = table;
                int len = tab.length;
    
                while (e != null) {
                    ThreadLocal<?> k = e.get();
                    if (k == key)
                        return e;
                    if (k == null)
                        expungeStaleEntry(i);
                    else
                        i = nextIndex(i, len);
                    e = tab[i];
                }
                return null;
            }
    

    看下ThreadLocalMap中的Entry源码:

     static class ThreadLocalMap {
    
    
            static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    }
    

    可以看到Entry继承了弱引用,当ThreadLocal没有被引用时,垃圾回收会将线程中ThreadLocalMap中的key(即为ThreadLocal)回收掉。

    总结:

    1. ThreadLocal提供线程内部的局部变量,保证线程安全
    2. ThreadLocal底层实现是在当前线程中有个ThreadLocalMap,以ThreadLocal为key放入entry中,通过entry.get()和entry.value获取key和值。
    3. ThreadLocalMap实现类似HashMap,但不完全一样。首先是根据key的hash计算在数组上的下标,在此下标以后进行循环查找或者插入,判断是否是通过一个的key的方法是根据key的引用地址判断。
    4. entry的弱引用内存泄漏问题,调用ThreadLocal的get和put方法时会自动清除无效的entry,但是为了保证不出现内存泄漏,需要在不需要使用时显示调用ThreadLocal.remove()方法。
    5. 获取ThreadLocal的hash值不是调用hashCode()方法,是使用内部的threadLocalHashCode,每个ThreadLocal对象的threadLocalHashCode值是不一样的,实例化ThreadLocal时会生成一个hash值。

    相关文章

      网友评论

          本文标题:ThreadLocal探究

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