ThreadLocal这个类给线程提供了一个本地变量,这个变量是该线程自己拥有的。在该线程存活和ThreadLocal实例能访问的时候,保存了对这个变量副本的引用.当线程消失的时候,所有的本地实例都会被GC。建议ThreadLocal最好是 private static 修饰的成员。
1 2 3 4 5 6 7 8 9 10 public class ThreadLocal <T> { private final int threadLocalHashCode = nextHashCode(); private static AtomicInteger nextHashCode = new AtomicInteger (); private static final int HASH_INCREMENT = 0x61c88647 ; private static int nextHashCode () { return nextHashCode.getAndAdd(HASH_INCREMENT); }
ThreadLocals 解决Hash 冲突使用线性探测的方法。其中key为ThreadLocal对象,因为Thread类中的threadLocals是个map对象,意味一个Thread中可以存放多个ThreadLocal对象。因此用threadLocalHashCode来区分是哪个ThreadLocal类型。
get方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 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变量,如果这个变量对当前线程没有值,则它调用前面介绍的initialValue方法获取初始值。
1 2 3 ThreadLocalMap getMap (Thread t) { return t.threadLocals; }
Thread中threadLocals定义:
1 ThreadLocal.ThreadLocalMap threadLocals = null ;
ThreadLocalMap结构如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 static class ThreadLocalMap { static class Entry extends WeakReference <ThreadLocal<?>> { Object value; Entry(ThreadLocal<?> k, Object v) { super (k); value = v; } } private static final int INITIAL_CAPACITY = 16 ; private Entry[] table; private int size = 0 ; private int threshold; private void setThreshold (int len) { threshold = len * 2 / 3 ; } private static int nextIndex (int i, int len) { return ((i + 1 < len) ? i + 1 : 0 ); } ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry [INITIAL_CAPACITY]; int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1 ); table[i] = new Entry (firstKey, firstValue); size = 1 ; setThreshold(INITIAL_CAPACITY); }
内部是Entry数组,Entry是继承WeakReference。ThreadLocalMap的getEntry方法:
1 2 3 4 5 6 7 8 private Entry getEntry (ThreadLocal<?> key) { 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); }
先结合threadLocalHashCode与table.length – 1的值确定该ThreadLocal在ThreadLocalMap的Entry数组下标位置。然后根据下标如果能在table找到直接返回,否则调用getEntryAfterMiss:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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 ; }
解释下e.get() == null表示这个key引用被垃圾回收了,因此需要调用expungeStaleEntry方法从数组里删除该Entry;如果其它情况需要向后找下一个Entry,因为采用的是线性探测法解决冲突的。
set方法:
1 2 3 4 5 6 7 8 9 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的set方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 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(); if (k == key) { e.value = value; return ; } if (k == null ) { replaceStaleEntry(key, value, i); return ; } } tab[i] = new Entry (key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
for循环中是根据计算得出的下标在table中找Entry,当Entry不为空时,如果是同一个对象,k == key,直接替换Entry里面的value值;如果k为null,说明k被回收了,重新放入该位置;否则向后继续找Entry,直到Entry为空。
如果计算后的坐标获取到的entry为null,就new一个Entry对象并保存进去,然后调用cleanSomeSlots()对table进行清理,如果没有任何Entry被清理,并且数组的size超过了阈值,就会调用rehash()方法。
cleanSomeSlots()中会调用expungeStaleEntry清理不用的Entry。rehash则会调用expungeStaleEntries()方法清理所有不用的Entry:
1 2 3 4 5 6 7 8 private void rehash () { expungeStaleEntries(); if (size >= threshold - threshold / 4 ) resize(); }