ThreadLocal源码解析

hash值的计算。

	//初始为0,静态变量。
	private static AtomicInteger nextHashCode = new AtomicInteger();
	
	/**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    //hash值每次递增该数值,关于为啥是这么个值使得hash可以分布更均匀,有兴趣的可以自行去深究。
	private static final int HASH_INCREMENT = 0x61c88647;
	
	//初始化时调用nextHashCode方法获得hash值
	private final int threadLocalHashCode = nextHashCode();
	//hash值每次递增HASH_INCREMENT
	private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

set方法

	public void set(T value) {
		//获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的threadLocals属性,初始为空
       ThreadLocalMap map = getMap(t);
        if (map != null)
        	//map已经初始化过,调用set方法设置值
            map.set(this, value);
        else
        	//初始化map
            createMap(t, value);
    }
    //返回类型为ThreadLocalMap(用于存储当前线程持有的所有threadlocal和value的映射关系)
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    //实例化ThreadLocalMap赋值给thread的threadLocals属性
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

ThreadLocal静态的内部类ThreadLocalMap

	static class ThreadLocalMap {
		//entry的key为threadlocal, 且key是一个弱引用
		// 由于 thread -> threadlocalmap -> entry -> threadlocal , 如果这里是强引用,则在thread销毁之前,threadlocal 永远不会被回收(此时该threadlocal对于使用者来说可能已经使用完成了)。
		// 然而value依然是强引用,还是有内存泄露的风险
		// 所以threadlocal 的 get(),set(),remove()方法 会对entry中key为空的value值的引用置空,使得value能够尽早被回收
		// 为了使得entry中的key为空,我们需要在使用完毕后,及时调用remove()方法
		static class Entry extends WeakReference<ThreadLocal<?>> {
            Object value;
			//存放threadlocal和value的映射关系
            Entry(ThreadLocal<?> k, Object v) {
            	//这里对threadlocal的引用 赋值给了父类Reference的referent属性
                super(k);
                value = v;
            }
        }
		//初始容量为16
		private static final int INITIAL_CAPACITY = 16;
		//存放entry的数组
		private Entry[] table;
		//当前map的数据量
		private int size = 0;
		//扩容阈值
		private int threshold;
		ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
			//table数组初始容量为16
            table = new Entry[INITIAL_CAPACITY];
            //使用hash值 & 数组长度-1 ,得到落在数组中的下标。
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            //在数组对应位置上,实例化一个entry
            table[i] = new Entry(firstKey, firstValue);
            //当前数据数量为1
            size = 1; 
            //设置扩容阈值
            setThreshold(INITIAL_CAPACITY);
        }
        //threshold 为数组长度的2/3 , 扩容时使用该变量
        private void setThreshold(int len) {
            threshold = len * 2 / 3;
        }
        private void set(ThreadLocal<?> key, Object value) {
			//首先获取数组
            Entry[] tab = table;
            //获取数组的长度
            int len = tab.length;
            //与运算计算在数组中的下标i
            int i = key.threadLocalHashCode & (len-1);
			
			//获取数组i位置上的数据 , 如果i上已经有数据,则调用nextIndex获取下一个下标位置(线性探测可用的位置)
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                //获取entry的referent,就是对threadLocal的引用(当threadlocal对象被回收时,则返回的referent为null)
                ThreadLocal<?> k = e.get();
				//相同的key,直接替换value
                if (k == key) {
                    e.value = value;
                    return;
                }
				//threadlocal被回收了
                if (k == null) {
                	//清除i下标无用的entry,并设置当前的threadlocal和value为entry到数组中。
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }
			//i位置上没有数据,直接实例化entry,设置到数组上
            tab[i] = new Entry(key, value);
            //数据量加一
            int sz = ++size;
            //cleanSomeSlots方法,清除没用的entry(key为空,也就是对threadlocal的引用为空)。
            //如果没有数据被清除。则判断当前容量是否大于等于threshold,如果成立,则调用rehash,看是否需要扩容。
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
        //判断下一个位置的下标是否小于数组长度,小于则为0,否则返回下一个位置
        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }
        //判断上一个位置的下标是否大于等于0,成立,则返回上一个位置的下标,否则返回数组尾部的下标
        private static int prevIndex(int i, int len) {
            return ((i - 1 >= 0) ? i - 1 : len - 1);
        }
        //清除无用entry,并将key,value包装为entry设置到数组中
        //staleSlot是无用的entry的下标位置
        private void replaceStaleEntry(ThreadLocal<?> key, Object value,
                                       int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;
            Entry e;
			//slotToExpunge赋值为staleSlot
            int slotToExpunge = staleSlot;
            //循环查找staleSlot之前位置的数据
            for (int i = prevIndex(staleSlot, len);
            	 //直到找到的数据为空,停止循环
                 (e = tab[i]) != null;
                 i = prevIndex(i, len))
                //如果该位置entry的referent为空,则slotToExpunge记录该位置的下标
                if (e.get() == null)
                    slotToExpunge = i;

            //循环查找staleSlot之后位置的数据
            for (int i = nextIndex(staleSlot, len);
            	 //直到找到的数据为空,停止循环
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                //获取获取entry的referent
                ThreadLocal<?> k = e.get();
				
				//如果找到的k和当前数据key相同
                if (k == key) {
                	//将原本value替换为新的value
                    e.value = value;
					//将i位置的entry赋值为staleSlot位置的entry(无用的) 此时i位置上的entry是无用的
                    tab[i] = tab[staleSlot];
                    //将staleSlot位置赋值为有效的本次有效的entry
                    tab[staleSlot] = e;

                    // Start expunge at preceding stale entry if it exists
                    //如果slotToExpunge 和staleSlot相等 ,将slotToExpunge 修改为i
                    if (slotToExpunge == staleSlot)
                        slotToExpunge = i;
                    //expungeStaleEntry清除slotToExpunge位置的数entry,返回的是遍历到的没数据的位置的下标
                    //在使用返回的下标调用cleanSomeSlots方法,判断是否还要接着清理无用的entry
                    cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
                    //直接返回
                    return;
                }

                //如果找到的i下标的位置数据无用 且 slotToExpunge 和staleSlot相等,将slotToExpunge 修改为i
                if (k == null && slotToExpunge == staleSlot)
                    slotToExpunge = i;
            }

            //没有找到持有相同的threadlocal的数据,直接将staleSlot位置的原entry的value置为空
            tab[staleSlot].value = null;
            //包装为entry设置到数组staleSlot位置上
            tab[staleSlot] = new Entry(key, value);

            //slotToExpunge 和 staleSlot 不等,则进行无用数据清理
            if (slotToExpunge != staleSlot)
                cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
        }
        //判断是否有需要清除的entry
        private boolean cleanSomeSlots(int i, int n) {
            boolean removed = false;
            Entry[] tab = table;
            int len = tab.length;
            do {
            	//获取下一个位置的下标
                i = nextIndex(i, len);
                //获取到该下标的entry
                Entry e = tab[i];
                //如果entry不为空且entry的referent为空(当threadlocal对象被回收时,则此时返回的referent为null)
                if (e != null && e.get() == null) { 
                    n = len;
                    //有无用的entry可以移除,removed 修改为true
                    removed = true;
                    //对该下标的数据进行清理
                    i = expungeStaleEntry(i);
                }
            } while ( (n >>>= 1) != 0);  //无符号右移1位,直到为0,跳出循环。为了不过多影响插入的性能,这里并不会扫描全部的元素。
            return removed;
        }
        //清理下标位置的数据,还会一起清除其他无用的entry
	    private int expungeStaleEntry(int staleSlot) {
            Entry[] tab = table;
            int len = tab.length;

            //将该位置的value置空
            tab[staleSlot].value = null;
            //将该位置置空
            tab[staleSlot] = null;
            //容量减一
            size--;

            // Rehash until we encounter null
            Entry e;
            int i;
            //接着寻找下一个位置
            for (i = nextIndex(staleSlot, len);
            	 //获取到下一个位置的entry,直到下一个位置的没有数据
                 (e = tab[i]) != null;
                 i = nextIndex(i, len)) {
                //获取到entry的referent属性,也就是对threadlocal的引用
                ThreadLocal<?> k = e.get();
                //为空,则说明threadlocal对象被回收了,则接着清空value和entry
                if (k == null) {
                    e.value = null;
                    tab[i] = null;
                    size--;
                } 
                //不为空
                else {
                	//使用hash计算出下标
                    int h = k.threadLocalHashCode & (len - 1);
                    //如果不等于当前数据处于的下标位置i,则说明之前发生了hash冲突,h是原本数据应该存放的下标位置,i则是线性探测之后的下标位置
                    //为了让数据尽可能在原本hash值计算出来的位置,减少和之后其他数据的碰撞概率
                    if (h != i) {
                    	//将数组当前下标位置置空
                        tab[i] = null;

                        // Unlike Knuth 6.4 Algorithm R, we must scan until
                        // null because multiple entries could have been stale.
                        //判断h位置是否有值,如果有值,接着找下一个下标,直到找到可以使用的下标
                        while (tab[h] != null)
                            h = nextIndex(h, len);
                        //将entry设置到h位置
                        tab[h] = e;
                    }
                }
            }
            //返回for循环中找到的没有数据的位置的下标
            return i;
        }
    	private void rehash() {
			//判断是否有无用的entry是否要清理,如果有则清理
	        expungeStaleEntries();
			
			//如果当前容量大于等于 (hreshold - threshold / 4)
	        if (size >= threshold - threshold / 4)
	        	//调用resize扩容
	            resize();
    	}
	    private void expungeStaleEntries() {
	        Entry[] tab = table;
	        int len = tab.length;
	        //遍历数组
	        for (int j = 0; j < len; j++) {
	            Entry e = tab[j];
	            //如果entry的referent为空,调用expungeStaleEntry方法清除无用的entry
	            if (e != null && e.get() == null)
	                expungeStaleEntry(j);
	        }
	    }
    	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];
                //获取entry
                if (e != null) {
                    ThreadLocal<?> k = e.get();
                    //如果entry的referent为空,则接着清除entry的value
                    if (k == null) {
                        e.value = null; // Help the GC
                    } 
                    else {
                    	//计算新的下标值
                        int h = k.threadLocalHashCode & (newLen - 1);
                        //hash冲突,查找下一个位置
                        while (newTab[h] != null)
                            h = nextIndex(h, newLen);
                        //将entry赋值到新数组上
                        newTab[h] = e;
                        count++;
                    }
                }
            }
			
			//设置新的threshold和size 
            setThreshold(newLen);
            size = count;
            //设置为新的tab
            table = newTab;
        }   
	}
	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);
    }
    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;
            //清楚key为空的数据
            if (k == null)
                expungeStaleEntry(i);
            //找下一个位置
            else
                i = nextIndex(i, len);
            e = tab[i];
        }
        return null;
    }

再来看到get方法

	public T get() {
		//获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的threadLocals属性
        ThreadLocalMap map = getMap(t);
        //不为空
        if (map != null) {
        	//获取到entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            //如果entry不为空,获取对应的value
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        //map为空或者对应的entry为空,初始化该threadlocal的entry
        return setInitialValue();
    }
    private T setInitialValue() {
    	//调用initialValue方法获取初始默认值
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        //如果map不为空,调用set方法设置值
        if (map != null)
            map.set(this, value);
        //初始化map,并设置值
        else
            createMap(t, value);
        //返回alue
        return value;
    }
    //默认返回null,可以重写来返回一个初始的默认值
    protected T initialValue() {
        return null;
    }

看到remove方法

 	public void remove() {
 		 //获取threadLocals,如果不为空,移除对应的entry
         ThreadLocalMap m = getMap(Thread.currentThread());
         //如果threadLocals不为空,从threadLocals移除该threadlocal对应的数据
         if (m != null)
             m.remove(this);
    }
    private void remove(ThreadLocal<?> key) {
        Entry[] tab = table;
        int len = tab.length;
        //使用hash值计算到下标值i
        int i = key.threadLocalHashCode & (len-1);
        //从数组的i位置开始向后遍历,找到数据
        for (Entry e = tab[i];
             e != null;
             e = tab[i = nextIndex(i, len)]) {
                //entry的threadlocal和移除的key相同
                if (e.get() == key) {
                //将entry的referent置空
                e.clear();
                //清除i下标位置的数据
                expungeStaleEntry(i);
                return;
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值