概述
从 ThreadLocal 源码学习可知,ThreadLocalMap 是 ThreadLocal 的静态内部类。
而每个 Thread 都有自己的 ThreadLocalMap,Map 中的 key 为 ThreadLocal 变量,value 为 ThreadLocal 的值。不同线程的 ThreadLocalMap 互相不可见,也就保证了线程安全。
public class ThreadLocal<T> {
static class ThreadLocalMap {
}
}
ThreadLocalMap 的成员变量
ThreadLocalMap 用 Entry 数组保存键值对,键为当前的 ThreadLocal 对象,值为 ThreadLocal 对象对应的值
Entry 数组默认的长度为 16,其中阈值 threshold 默认为 INITIAL_CAPACITY *2/3
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold; // Default to 0
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
}
ThreadLocalMap 构造方法
ThreadLocalMap 默认的构造方法,传入的 key 为当前 ThreadLocal 对象,firstValue 为 ThreadLocal 初始设置的值。
hashcode & (len - 1) 其实就是取模运算,定位元素在数组中的位置
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); // 对长度取模,得到索引位置
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY); // 初始化阈值,超过这个长度需要扩容
}
private void setThreshold(int len) {
threshold = len * 2 / 3; // 默认为数组长度的 2/3
}
// ThreadLocal 中的方法
private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
其次,ThreadLocalMap 的构造方法还支持传入其它的 ThreadLocalMap 进行初始化
大致原理为:新建一个和原来长度一样的 Entry 数组,遍历原数组,排除 key = null 的节点,放到新的 Entry 数组中。
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table; // 传入进来的数组
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j]; // 对原数组进行遍历
if (e != null) { // 节点不为空
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) { // entry 元素的 key 不为空时
Object value = key.childValue(e.value); // 其实是返回 ThreadLocal 对应的值
Entry c = new Entry(key, value); // 新建 Entry 节点
int h = key.threadLocalHashCode & (len - 1); // 取模
while (table[h] != null) // 数组已有元素,产生冲突
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
// 返回当前 i 的后一位,若 i 已经是最后一位,返回第 0 位,依次循环
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
成员方法
getEntry
根据 ThreadLocal 获取 entry 节点,若 entry 为 null,那么直接返回 null;
若 entry 中保存的 threadlocal 变量和传进来的 threadlocal 变量不一致,那么向后遍历下一个节点,直到找到相同的节点;
若没有相同节点,找到 key = null 的节点那么返回 null。
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
// 没找到节点返回 null
// 数组中的 entry 和 threadLocal 不一致
return getEntryAfterMiss(key, i, e);
}
// 顺序向后遍历,直到找到满足的点
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) { // 节点为 null 直接返回 null
ThreadLocal<?> k = e.get(); // k 为 entry 数组中的 entry 对应的 key
if (k == key)
return e;
if (k == null) // 若 key 被置为空
expungeStaleEntry(i); // 清除 key 为 null 的节点
else
i = nextIndex(i, len); // 找到下一个节点
e = tab[i];
}
return null;
}
expungeStaleEntry
staleSlot 表示过时的插槽,方法的意思在下面有注释,主要包含:
- 清除 key = null 的 entry,便于垃圾回收
2. 从 staleSlot 位置开始遍历 entry 数组,对数组中元素进行 rehash 操作,若遍历到的数组的 key = null,结束遍历。
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
tab[staleSlot].value = null; // 将 staleSlot 处的 key,value 都置为 null,方便垃圾回收
tab[staleSlot] = null;
size--;
Entry e;
int i;
// 开始进行 rehash 操作,即遍历数组直至遇到 key = null
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
// 遍历 staleSlot 后面的节点
ThreadLocal<?> k = e.get();
if (k == null) { // 删除 key = null 的节点
e.value = null;
tab[i] = null;
size--;
} else {
// 重新计算该 key 对应数组中的位置
int h = k.threadLocalHashCode & (len - 1);
if (h != i) { // 节点不一致,表示需要迁移
tab[i] = null; // 原来的位置置为 null
while (tab[h] != null) // 找到第一个为 null 的位置,插入元素
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
set
将键值对 set 到 entry 数组中
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
// 获取 key 对应数组的位置
int i = key.threadLocalHashCode & (len-1);
// 遍历数组
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
// 获取数组中的 entry 的 ThreadLocal 引用
ThreadLocal<?> k = e.get();
// 如果 key 一样,覆盖 value
if (k == key) {
e.value = value;
return;
}
// 传进来的 key 为 null,将 i 处的节点设为 key-value,并清除后面 key = null 的节点
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
// 超过阈值,进行 rehash
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
replaceStaleEntry
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
int staleSlot) {
Entry[] tab = table;
int len = tab.length;
Entry e;
// 当前需要清除的节点位置
int slotToExpunge = staleSlot;
// 从当前节点往前遍历,找到 value 为 null 的位置
for (int i = prevIndex(staleSlot, len);
(e = tab[i]) != null;
i = prevIndex(i, len))
if (e.get() == null)
slotToExpunge = i;
// 从 staleSlot 位置开始往后遍历
for (int i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get(); // 获取 threadlocal 的引用
if (k == key) {
e.value = value;
tab[i] = tab[staleSlot];
tab[staleSlot] = e;
// Start expunge at preceding stale entry if it exists
if (slotToExpunge == staleSlot)
slotToExpunge = i;
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
return;
}
// If we didn't find stale entry on backward scan, the
// first stale entry seen while scanning for key is the
// first still present in the run.
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
}
// If key not found, put new entry in stale slot
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value);
// If there are any other stale entries in run, expunge them
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
ThreadLocal继承性解决方案https://blog.csdn.net/weixin_42200859/article/details/105396338