ThreadLocal源码解析
ThreadLocal简介
ThreadLocal是一个本地线程副本变量工具类,在每一个线程中都创建一个ThreadLocalMap对象,简单来说ThreadLocal就是一种以空间换取时间的做法,
每个线程都可以访问自己内部的ThreadLocalMap对象的value,通过这种方式避免资源在多线程之间共享。
常用于解决共享变量属性的线程安全问题。
ThreadLocal的set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
getMap和createMap都挺简单的,所以不详细说,下面介绍ThreadLocalMap的set方法。
ThreadLocalMap的set方法
public class ThreadLocal<T> {
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
static class ThreadLocalMap {
private Entry[] table;
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();
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();
}
private void replaceStaleEntry(ThreadLocal<?> key, Object value,
int staleSlot) {
Entry[] tab = table;
int len = tab.length;
Entry e;
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);
}
}
}
总结
每个线程都有一个自己的ThreadLocalMap,每个ThreadLocalMap里边都维护了一个属于自己的Entry数组,填充这个数组的方式和填充HashMap的方式
差不多(它相同值时替换,HashMap是添加到链表或者树)。同一个ThreadLocal的set会把上一次的值覆盖掉,也就是说,同一个线程的同一个ThreadLocal
进行set操作,会把之前的值覆盖掉。同一个Thread的不同ThreadLocal会均匀的分布在Entry数组里。