ThreadLocal源码分析

来源:互联网 发布:php时间戳精确到毫秒 编辑:程序博客网 时间:2024/06/16 07:05
       ThreadLocal这个类给线程提供了一个本地变量,这个变量是该线程自己拥有的。在该线程存活和ThreadLocal实例能访问的时候,保存了对这个变量副本的引用.当线程消失的时候,所有的本地实例都会被GC。建议ThreadLocal最好是 private static 修饰的成员。
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是个ThreadLocalMap对象,意味一个Thread中可以存放多个ThreadLocal对象。因此用threadLocalHashCode来区分是哪个ThreadLocal类型。

get方法:
    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方法获取初始值。
    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }
       Thread中threadLocals定义:
    ThreadLocal.ThreadLocalMap threadLocals = null;

       ThreadLocalMap结构如下:
    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;            }        }                private static final int INITIAL_CAPACITY = 16;        private Entry[] table;        private int size = 0;        private int threshold; // Default to 0        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方法:
        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:
        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方法:
    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方法如下:
        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:
        private void rehash() {            expungeStaleEntries();            // Use lower threshold for doubling to avoid hysteresis            if (size >= threshold - threshold / 4)                resize();        }



原创粉丝点击