深入解析ThreadLocal( 十 )


(1) 首先从ThreadLocal的set() 方法入手
public void set(T value) {Thread t = Thread.currentThread();ThreadLocal.ThreadLocalMap map = getMap(t);if (map != null)//调用了ThreadLocalMap的set方法map.set(this, value);elsecreateMap(t, value);}ThreadLocal.ThreadLocalMap getMap(Thread t) {return t.threadLocals;}void createMap(Thread t, T firstValue) {//调用了ThreadLocalMap的构造方法t.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);}这个方法我们刚才分析过, 其作用是设置当前线程绑定的局部变量 :
A. 首先获取当前线程 , 并根据当前线程获取一个Map
B. 如果获取的Map不为空 , 则将参数设置到Map中(当前ThreadLocal的引用作为key)
(这里调用了ThreadLocalMap的set方法)
C. 如果Map为空 , 则给该线程创建 Map , 并设置初始值
(这里调用了ThreadLocalMap的构造方法)
这段代码有两个地方分别涉及到ThreadLocalMap的两个方法, 我们接着分析这两个方法 。
(2)构造方法`ThreadLocalMap(ThreadLocal firstKey, Object firstValue)`
/** firstKey : 本ThreadLocal实例(this)* firstValue : 要保存的线程本地变量*/ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {//初始化tabletable = new ThreadLocal.ThreadLocalMap.Entry[INITIAL_CAPACITY];//计算索引(重点代码)int i = firstKey.threadLocalHashCode//设置值table[i] = new ThreadLocal.ThreadLocalMap.Entry(firstKey, firstValue);size = 1;//设置阈值setThreshold(INITIAL_CAPACITY);}构造函数首先创建一个长度为16的Entry数组 , 然后计算出firstKey对应的索引 , 然后存储到table中 , 并设置size和threshold 。
重点分析: int i = firstKey.threadLocalHashCode--tt-darkmode-bgcolor: #1B1B1B;">firstKey.threadLocalHashCode:
private final int threadLocalHashCode = nextHashCode();private static int nextHashCode() {return nextHashCode.getAndAdd(HASH_INCREMENT);}//AtomicInteger是一个提供原子操作的Integer类 , 通过线程安全的方式操作加减,适合高并发情况下的使用private static AtomicInteger nextHashCode =new AtomicInteger();//特殊的hash值private static final int HASH_INCREMENT = 0x61c88647;这里定义了一个AtomicInteger类型 , 每次获取当前值并加上HASH_INCREMENT , HASH_INCREMENT = 0x61c88647,这个值跟斐波那契数列(黄金分割数)有关 , 其主要目的就是为了让哈希码能均匀的分布在2的n次方的数组里, 也就是Entry[] table中 , 这样做可以尽量避免hash冲突 。
b. 关于int len = tab.length;//计算索引(重点代码 , 刚才分析过了)int i = key.threadLocalHashCode/*** 使用线性探测法查找元素(重点代码)*/for (ThreadLocal.ThreadLocalMap.Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {ThreadLocal k = e.get();//ThreadLocal 对应的 key 存在 , 直接覆盖之前的值if (k == key) {e.value = http://kandian.youth.cn/index/value;return;}// key为 null , 但是值不为 null , 说明之前的 ThreadLocal 对象已经被回收了 ,// 当前数组中的 Entry 是一个陈旧(stale)的元素if (k == null) {//用新元素替换陈旧的元素 , 这个方法进行了不少的垃圾清理动作 , 防止内存泄漏replaceStaleEntry(key, value, i);return;}}//ThreadLocal对应的key不存在并且没有找到陈旧的元素 , 则在空元素的位置创建一个新的Entry 。tab[i] = new Entry(key, value);int sz = ++size;/*** cleanSomeSlots用于清除那些e.get()==null的元素 ,* 这种数据key关联的对象已经被回收 , 所以这个Entry(table[index])可以被置null 。* 如果没有清除任何entry,并且当前使用量达到了负载因子所定义(长度的2/3) , 那么进行* rehash(执行一次全表的扫描清理工作)*/if (!cleanSomeSlots(i, sz) } /*** 获取环形数组的下一个索引*/private static int nextIndex(int i, int len) {return ((i + 1


推荐阅读