ThreadLocal源码分析

来源:互联网 发布:安卓玩java游戏 编辑:程序博客网 时间:2024/06/07 03:12

ThreadLocal是java多线程编程中非常重要的一个类。ThreadLocal存放的变量只能由相应的线程进行访问,即ThreadLocal为每个线程存放一个变量的副本
下面对ThreadLocal进行源码分析

Thread 类

查看Thread类的源代码,可以看到Thread类中有个

ThreadLocal.ThreadLocalMap threadLocals = null;

此变量没有声明为private,而是使用了默认的类型friendly,即同一个包下下的类可以看到。
其实 线程本地变量就存储在threadLocals map中。

ThreadLocal 类

public class ThreadLocal<T> {    /**     * ThreadLocals 解决Hash 冲突使用线性探测的方法。     * 其中key为ThreadLocal对象。因为Thread类中的           threadLocals是个map对象。     * 意外这一个Thread中可以存放多个ThreadLocal对象。所以Key为ThreadLocal,value为需要存放的值。     * 把threadLocalHashCode声明为final,所以可以作为 ThreadLocalMap中的key,保持不变。     * 调用nextHashCode()使得每次把共享的类变量值+1;     */    private final int threadLocalHashCode = nextHashCode();    /**     *  nextHashCode声明为static,代表类变量。默认初始值为   0。     * 使用原子整数类作为下一个hashcode值。由于是个类变量,所以当jvm把ThreadLocal加载到内存中时。     * nextHashCode只有一份,是线程共享的。     */    private static AtomicInteger nextHashCode =        new AtomicInteger();    /**     *由于采用线性探测,所以代表下一个值在当前值得hash增长。     */    private static final int HASH_INCREMENT = 0x61c88647;    /**     * 返回一下hashcode     */    private static int nextHashCode() {        return nextHashCode.getAndAdd(HASH_INCREMENT);    }

initialValue()方法

/**     *这个方法返回当前线程threadLocal的初始变量值。     *这个方法的调用出现在ThreadLocal调用get()方法,然后之前没有调用ThreadLocal的set()方法。     *这个方法被当前线程调用一次或者调用多次。当调用ThreadLocal的remove()方法后,可能会再次调用此方法。     *这个方法默认返回Null,如果程序员想让它返回一个初始值,通过一个匿名内部类重写此方法。     *如:     ThreadLocal<Integer> local      = new   ThreadLocal<Integer>(){            @Override            protected Integer initialValue() {                return 0;            }        };     */    protected T initialValue() {        return null;    }

get()方法

 /**     * 返回当前线程的threadLocal变量的拷贝值。     * 如果这个变量对当前线程没有值,则它调用前面介绍的initialValue方法获取初始值。     */    public T get() {        Thread t = Thread.currentThread();        //依据当前线程获取ThreadLocalMap        /**         * 获取Thread对象的threadLocals属性         * ThreadLocalMap getMap(Thread t) {            return t.threadLocals;           }        */        ThreadLocalMap map = getMap(t);        if (map != null) {            /**             *由于一个thread对象可以存放多个ThreadLocal变量,因此使用ThreadLocal作为key值。                 */            ThreadLocalMap.Entry e = map.getEntry(this);            if (e != null) {                @SuppressWarnings("unchecked")                T result = (T)e.value;                return result;            }        }        return setInitialValue();    }

ThreadLocalMap数据结构

 static class ThreadLocalMap {        /**         * 这个entry继承WeakReference,为了避免当内存不够用被回收。         * 注意null keys(如:entry.get()==null)意外这这个Key不再引用,因此这个entry可以从这个表中被移除。         *          */        static class Entry extends WeakReference<ThreadLocal<?>> {            Object value;            Entry(ThreadLocal<?> k, Object v) {                super(k);                value = v;            }        }        /**         * table的初始化值,必须是2的幂。默认是16         */        private static final int INITIAL_CAPACITY = 16;        /**         * 这个表可以动态扩展         * 表的长度必须是2的幂         */        private Entry[] table;        /**         * 表中的元素的数量.         */        private int size = 0;        /**         * 当size大于threshode时,就需要进行扩容。类似于加载因子         */        private int threshold; // Default to 0        /**         * threadHold的设置值方法         */        private void setThreshold(int len) {            threshold = len * 2 / 3;        }        ...
getEntry(ThreadLocal key ) 方法
    private Entry getEntry(ThreadLocal<?> key) {            //获取hash 值code            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);        }     private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {            Entry[] tab = table;            int len = tab.length;            while (e != null) {                ThreadLocal<?> k = e.get();                //由于key为this对象。因此采用比较内存地址的方式                if (k == key)                    return e;                //如果key为null,则引用被垃圾回收,需要从表里删除                if (k == null)                    expungeStaleEntry(i);                else                    //把i值加1再做判断,直到找到对应的key值                    i = nextIndex(i, len);                e = tab[i];            }            return null;        }

expungeStaleEntry()源码分析

由于Key为WeakReference,因此当key被垃圾回收时,需要把entry从table中移除。并重新rehash整个数组
nextIndex函数的定义如下:

    private static int nextIndex(int i, int len) {         return ((i + 1 < len) ? i + 1 : 0);    }

在重新rehash的过程中,如果遇到key==null,则把value设置为null。
否则如果key != null,则hash,如果对应的hashcode !==i,则把tab[i] = null,并线性探测tab[h],直到tab[h]==null,并把此元素放入到tab[h]中。

   private int expungeStaleEntry(int staleSlot) {            Entry[] tab = table;            int len = tab.length;            // expunge entry at staleSlot            tab[staleSlot].value = null;            tab[staleSlot] = null;            size--;            // Rehash until we encounter null            Entry e;            int i;            for (i = nextIndex(staleSlot, len);                 (e = tab[i]) != null;                 i = nextIndex(i, len)) {                ThreadLocal<?> k = e.get();                if (k == null) {                    e.value = null;                    tab[i] = null;                    size--;                } else {                    int h = k.threadLocalHashCode & (len - 1);                    if (h != i) {                        tab[i] = null;                        while (tab[h] != null)                            h = nextIndex(h, len);                        tab[h] = e;                    }                }            }            return i;        }
原创粉丝点击