ThreadLocal实现轻量数据共享的原理——源码
此类提供线程局部变量。 这些变量与普通变量不同,因为每个访问一个线程(通过其get或set方法)的线程都有其自己的,独立初始化的变量副本。 ThreadLocal实例通常是希望将状态与线程关联的类中的私有静态字段(例如,用户ID或事务ID)。
例如,下面的类生成每个线程本地的唯一标识符。 线程的ID是在第一次调用ThreadId.get()时分配的,并且在以后的调用中保持不变。
只要线程是活动的并且ThreadLocal实例是可访问的,则每个线程都对其线程局部变量的副本持有隐式引用。 线程消失后,其线程本地实例的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用)
案列
import java.util.concurrent.atomic.AtomicInteger;
public class ThreadId {
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId =
new ThreadLocal<Integer>() {
@Override protected Integer initialValue() {
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public static int get() {
return threadId.get();
}
}
属性
// 原子类 用于获取下一个 线程 hash 值 原子类-> 线程安全
private static AtomicInteger nextHashCode = new AtomicInteger();
// 线程 hash 增量步长 10 进制 : 1640531527 二进制: 1100001110010001000011001000111
// 将隐式顺序线程本地ID转换为用于2的幂次方表的近似最佳分布的乘法哈希值
private static final int HASH_INCREMENT = 0x61c88647;
private final int threadLocalHashCode = nextHashCode();
方法
// 获取下一个hashCode 使用原子类 增加 HASH_INCREMENT
private static int nextHashCode() {return nextHashCode.getAndAdd(HASH_INCREMENT);}
// 返回 当前 线程 默认值 , 每个线程最多调用一次 但当调用remove和get后可以再次调用
protected T initialValue() {return null;}
// ThreadLocalMap 在 Thread 类中进行保存
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
数据的操作委托给 ThreadLocalMap
ThreadLocalMap 主要用于数据存放
ThreadLocalMap是自定义的哈希映射,仅适用于维护线程局部值。 该类是包私有的,以允许声明Thread类中的字段。 为了帮助处理非常长的使用寿命,哈希表条目使用WeakReferences作为键。 但是,由于不使用参考队列,因此仅在表空间不足时,才保证删除过时的条目。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// 初始化大小 必须是 2 的幂次方
private static final int INITIAL_CAPACITY = 16;
// 保存数据表
private Entry[] table;
// 数据项
private int size = 0;
// 下次扩容 的阀值 默认0 --> 懒加载
private int threshold;
// 更新 扩容阀值
private void setThreshold(int len) {threshold = len * 2 / 3;}
// 下一个索引
private static int nextIndex(int i, int len) {return ((i + 1 < len) ? i + 1 : 0);}
// 前一个索引
private static int prevIndex(int i, int len) {return ((i - 1 >= 0) ? i - 1 : len - 1);}
// 带 数据的初始化方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
// hash 值 & 容量 - 1
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
// 重新构造 一个 map
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) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
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);
}
// key 不存在
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;
if (k == null)
expungeStaleEntry(i);
else // 探测 hash冲突 进行探测
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
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)]) {
ThreadLocal<?> k = e.get();
// 同一个 thredLocal 就直接替换
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold )
rehash();
}
// 删除 key
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();
expungeStaleEntry(i);
return;
}
}
}
// 替换槽位值
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
int staleSlot) {
Entry[] tab = table;
int len = tab.length;
Entry e;
// 找到前一个 槽位 这个槽位 未有数据 目的是 避免堆引用,GC回收
int slotToExpunge = staleSlot;
for (int i = prevIndex(staleSlot, len);
(e = tab[i]) != null;
i = prevIndex(i, len))
if (e.get() == null)
slotToExpunge = i;
// 找到槽位
for (int i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
// 如果找到 便 进行替换
if (k == key) {
e.value = value;
tab[i] = tab[staleSlot];
tab[staleSlot] = e;
// 清除槽位 如果存在陈旧的槽位
if (slotToExpunge == staleSlot)
slotToExpunge = i;
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
return;
}
// 运行中更新过时条目
if (k == null && slotToExpunge == staleSlot)
slotToExpunge = i;
}
// 如果不存在 , 则添加
tab[staleSlot].value = null;
tab[staleSlot] = new Entry(key, value);
// 清除过时条目
if (slotToExpunge != staleSlot)
cleanSomeSlots(expungeStaleEntry(slotToExpunge), len);
}
// 清空指定hash槽的值
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// 清空当前槽
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// 重新hash 知道遇到null
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
// 清空null 值
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
tab[i] = null;
// 找到下一个hash槽位 原因 是 在放值的时候采用的hash碰撞进行存放的
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
// 清空 槽位 返回 是否清除成功
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
Entry e = tab[i];
if (e != null && e.get() == null) {
n = len;
removed = true;
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);
return removed;
}
// 重新 hash
private void rehash() {
expungeStaleEntries();
// Use lower threshold for doubling to avoid hysteresis
if (size >= threshold - threshold / 4)
resize();
}
// 二倍扩容
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 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; // 置空 GC回收
} else {
int h = k.threadLocalHashCode & (newLen - 1);
// 碰撞法 解决hash冲突
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
setThreshold(newLen);
size = count;
table = newTab;
}
// 清空 所有 槽位
private void expungeStaleEntries() {
Entry[] tab = table;
int len = tab.length;
for (int j = 0; j < len; j++) {
Entry e = tab[j];
if (e != null && e.get() == null)
expungeStaleEntry(j);
}
}
总结
ThreadLocal 实现线程数据共享,自定义Map的实现,采用的Hash碰撞方法进行数据存储,原子类进行hash分配。也将数据绑定在线程上 , 在线程退出时进行数据销毁。