- ThreadLocalMap的数据结构是数组,数组元素是Entry,根据ThreadLocal对象的hashCode和数组长度求模算出对应的索引,如果数组对应的引用为null,则直接存储,否则,循环索引递增求模,直到数组对应索引为null存储;
- ThreadLocalMap有个很重要的概念,源码中是
run
,可以理解成“链”,即数组中连续不为null的部分称为“链”;
- ThreadLocalMap.set
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
//ThreadLocal对象的hashCode(定制)和数组长度求模,得到数组的索引;
int i = key.threadLocalHashCode & (len-1);
//从索引开始递增,直到索引对应的引用为null
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//ThreadLocal对象为static,所以直接用 == 做判断
if (k == key) {
//更新已存在的数据
e.value = value;
return;
}
//ThreadLocal对象已被回收
if (k == null) {
//替换Entry并清除已回收的ThreadLocal
replaceStaleEntry(key, value, i);
return;
}
}
//新创建Entry对象,放在末尾处
tab[i] = new Entry(key, value);
int sz = ++size;
//清除已回收ThreadLocal
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash(); //扩容
}
- 1.根据ThreadLocal的hashCode(定制hashCode)求模,算出对应的索引;
- 2.循环索引递增,如果Entry中的ThreadLocal匹配,则更新数据,如果Entry中ThreadLocal已被回收,则替换Entry并删除其他被回收的Entry;
- 3.如果没有找到Entry,则创建新的Entry并放在链的末尾;
- 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); //在链中寻找
}
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; //ThreadLocal匹配,直接返回
if (k == null)
expungeStaleEntry(i); //从i开始到链结束,删除已回收ThreadLocal,未被回收的重新求模存储;
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
- ThreadLocalMap.remove
private void remove(ThreadLocal<?> key) {
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)]) {
if (e.get() == key) {
e.clear();
//从i开始循环当前链,删除已回收ThreadLocal,未被回收的重新求模存储;
expungeStaleEntry(i);
return;
}
}
}
- ThreadLocalMap.resize
private void rehash() {
//删除所有已被回收的ThreadLocal,并重新求模存储
expungeStaleEntries();
// Use lower threshold for doubling to avoid hysteresis
if (size >= threshold - threshold / 4) //超过阈值的3/4,就扩容;阈值是数组长度的2/3;
resize(); //扩容
}
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2; //2倍扩容
Entry[] newTab = new Entry[newLen];
int count = 0;
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null; // Help the GC,处理已被回收的ThreadLocal
} else {
//重新求模存储
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
setThreshold(newLen);
size = count;
table = newTab;
}
- 扩容是按照原来数组长度的两倍扩容;触发扩容是大于等于3/4阈值,阈值是数组长度的2/3,触发扩容是数组长度的1/2;