车驰夜幕|面试时被问到ThreadLocal别慌,你要的答案都在这里( 四 )

replaceStaleEntry方法也会调用expungeStaleEntry方法 。
再看看setInitialValue方法中的createMap方法
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}代码很简单 , 就是new了一个ThreadLocalMap对象 。
好了 , 到这来get() 和 initialValue() 方法介绍完了 。
下面介绍set(T value) 方法
public void set(T value) {//获取当前线程 , 都是一样的套路Thread t = Thread.currentThread();//根据当前线程获取当中的ThreadLocalMapThreadLocalMap map = getMap(t);//ThreadLocalMap不为空 , 则调用之前介绍过的ThreadLocalMap的set方法if (map != null)map.set(this, value);else//如果ThreadLocalMap为空 , 则创建一个对象 , 之前也介绍过createMap(t, value);}so easy
最后 , 看看remove()方法
public void remove() {//还是那个套路 , 不过简化了一下//先获取当前线程 , 再获取线程中的ThreadLocalMap对象ThreadLocalMap m = getMap(Thread.currentThread());//如果ThreadLocalMap不为空if (m != null)//删除数据m.remove(this); }这个方法的关键就在于ThreadLocalMap类的remove方法
private void remove(ThreadLocal key) {//将table数组赋值给新数组tabEntry[] tab = table;//获取数组长度int len = tab.length;//跟之前一样计算数组中的下表int i = key.threadLocalHashCode//循环变量从下表i之后不为空的entryfor (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {//如果可以获取到threadLocal并且值等于keyif (e.get() == key) {//清空引用e.clear();//处理threadLocal为空但是value不为空的entryexpungeStaleEntry(i);return;}}}其中的clear方法 , 也很简单 , 只是把引用设置为null , 即清空引用
public void clear() {this.referent = null;}我们可以看到get()、set(T value) 和 remove()方法 , 都会调用expungeStaleEntry方法 , 我们接下来重点看一下expungeStaleEntry方法
private int expungeStaleEntry(int staleSlot) {Entry[] tab = table;int len = tab.length;//将位置staleSlot对应的entry中的value设置为null , 有助于垃圾回收tab[staleSlot].value = http://kandian.youth.cn/index/null;//将位置staleSlot对应的entry设置为null , 有助于垃圾回收tab[staleSlot] = null;//数组大小-1size--;Entry e;int i;//变量staleSlot之后entry不为空的数据for (i = nextIndex(staleSlot, len);(e = tab[i]) != null;i = nextIndex(i, len)) {//获取当前位置的entry中对应的threadLocalThreadLocal k = e.get();//threadLocal为空 , 说明是脏数据if (k == null) {//value设置为null , 有助于垃圾回收e.value = http://kandian.youth.cn/index/null;//当前位置的entry设置为nulltab[i] = null;//数组大小-1size--;} else {//重新计算位置int h = k.threadLocalHashCode//如果h和i不相等 , 说明存在hash冲突//现在它前面的脏Entry被清理//该Entry需要向前移动 , 防止下次get()或set()的时候//再次因散列冲突而查找到null值if (h != i) {tab[i] = null;while (tab[h] != null)h = nextIndex(h, len);tab[h] = e;}}}return i;}该方法首先清除当前位置的脏Entry , 然后向后遍历直到table[i]==null 。 在遍历的过程中如果再次遇到脏Entry就会清理 。如果没有遇到就会重新变量当前遇到的Entry , 如果重新散列得到的下标h与当前下标i不一致 , 说明该Entry被放入Entry数组的时候发生了散列冲突(其位置通过再散列被向后偏移了) , 现在其前面的脏Entry已经被清除 , 所以当前Entry应该向前移动 , 补上空位置 。 否则下次调用set()或get()方法查找该Entry的时候会查找到位于其之前的null值 。


推荐阅读