ThreadLocal 通过在每个 Thread 内部维护独立的 ThreadLocalMap
实现线程安全,每个线程只能访问自己的数据副本,避免了线程间的数据竞争。
Thread 与 ThreadLocal 的关联
// Thread 类中的字段
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
ThreadLocal.ThreadLocalMap terminatingThreadLocals = null;
设计原理:
ThreadLocalMap
数据隔离设计
private T get(Thread t) {
ThreadLocalMap map = getMap(t); // 获取当前线程的Map
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T) e.value;
return result;
}
}
return setInitialValue(t);
}
线程安全关键点:
ThreadLocalMap
getMap(t)
获取的是线程 t
专属的映射表黄金分割哈希算法
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
算法亮点:
0x61c88647
是黄金分割数的整数表示,0x61c88647
(2^32⋅ϕ−1)WeakReference 内存管理
static class Entry extends WeakReference> {
Object value;
Entry(ThreadLocal> k, Object v) {
super(k); // ThreadLocal作为弱引用的key
value = v;
}
}
设计优势:
expungeStaleEntry()
方法清理失效条目开放地址法哈希冲突解决
private void set(ThreadLocal> key, Object value) {
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.refersTo(key)) {
e.value = value;
return;
}
if (e.refersTo(null)) {
replaceStaleEntry(key, value, i);
return;
}
}
// ...
}
冲突处理策略:
nextIndex(i, len)
循环查找下一个位置replaceStaleEntry()
cleanSomeSlots()
进行启发式清理智能的三种线程局部存储类型
根据代码分析,Thread 支持三种不同的 ThreadLocal 映射:
ThreadLocalMap getMap(Thread t) {
if (this instanceof TerminatingThreadLocal) {
return t.terminatingThreadLocals();
} else {
return t.threadLocals();
}
}
类型划分:
threadLocals
中inheritableThreadLocals
中,子线程可继承父线程的值,实际上就是在构造函数内 Thread(ThreadGroup g, String name, int characteristics, Runnable task, long stackSize) {
// ...
if (!attached) {
if ((characteristics & NO_INHERIT_THREAD_LOCALS) == 0) {
ThreadLocal.ThreadLocalMap parentMap = parent.inheritableThreadLocals;
if (parentMap != null && parentMap.size() > 0) {
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parentMap);
}
// ...
}
}
}
!attached
为 true
,表示当前线程尚未附加到线程组,每个线程组都有自己的资源和管理方式,不需要继承其他线程组的属性。NO_INHERIT_THREAD_LOCALS
是一个常量,用于表示不继承线程局部变量的特性。
terminatingThreadLocals
中,线程结束时特殊处理,调用TerminatingThreadLocal.threadTerminated(),可以自定义接口处理释放虚拟线程优化支持
T getCarrierThreadLocal() {
assert this instanceof CarrierThreadLocal;
return get(Thread.currentCarrierThread());
}
现代化设计:
CarrierThreadLocal
绑定到载体线程而非虚拟线程expungeStaleEntry
(int staleSlot)这个方法的主要目的是从ThreadLocalMap
中移除一个无效(stale)的条目,并重新哈希可能发生冲突的条目。这个方法在ThreadLocalMap
的set
和remove
操作中调用,以确保哈希表保持有效状态。
获取哈希表和长度:
Entry[] tab = table;
int len = tab.length;
移除无效条目:
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
重新哈希可能冲突的条目:
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal> k = e.get();
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
tab[i] = null;
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
关键点解释:
ThreadLocal
对象被垃圾回收后,它的引用会变成null
,这样的条目被称为无效条目。null
。性能设计
内存管理
这种设计使得 ThreadLocal 在保证完全线程安全的同时,实现了极高的访问性能和优雅的内存管理。