ThreadLocal简介
每个java线程都持有一个ThreadLocal.ThreadLocalMap对象,这个map对象中,ThreadLocal作为键,需要保证线程安全的变量作为值。每个线程给变量设值时,实际上是给线程本身持有的ThreadLocalMap对象put键值对。获取变量值时,实际是以ThreadLocal对象为键,获取线程本身持有的ThreadLocalMap中的值。
Thread中的ThreadLocalMap成员变量:
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal中的set方法
public void set(T value) {。
//获取当前线程
Thread t = Thread.currentThread();
//通过当前线程获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
//判断是否初始化
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
getMap()方法:
//由此方法可以看出,每次获取Map的时候,是获取传入线程中的threadLocals成员变量
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocal的get方法
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的ThreadLocalMap 成员变量
ThreadLocalMap map = getMap(t);
if (map != null) {
//根据键获取值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//变量没有初始化则初始化并返回
return setInitialValue();
}
private T setInitialValue() {
//初始化空值
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
现在,ThreadLocal我们已经了解的差不多了,下来看看ThreadLocalMap的实现。
ThreadLocalMap是ThreadLocal内部的一个Map实现,然而它没有实现任何集合的接口规范,因为它仅供ThreadLocal内部使用,数据结构采用数组+开方定址法,Entry继承WeakRefrence。
成员变量的定义:
//Entry的定义,继承WeakReference的目的是让key生存周期只能活到下次GC前
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//初始化容量大小为16
private static final int INITIAL_CAPACITY = 16;
//数组,长度是2的整数次幂
private Entry[] table;
//map的大小
private int size = 0;
//扩容阈值
private int threshold; // Default to 0
Map的set()方法
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;
//不影响map的大小,故方法到此结束
return;
}
//发现key已经被回收或者初始化,则在此地放入键值
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//若桶为空,直接赋值
tab[i] = new Entry(key, value);
int sz = ++size;
//没能清理掉一个过时的entry,并且map大小超过阈值则扩容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
清理空slot
private boolean cleanSomeSlots(int i, int n) {
//首先定义返回值为false,表示清理不到任何东西
boolean removed = false;
ThreadLocal.ThreadLocalMap.Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
ThreadLocal.ThreadLocalMap.Entry e = tab[i];
//找到可以清理的entry
if (e != null && e.get() == null) {
n = len;
removed = true;
//调用清理方法
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);
return removed;
}
ThreadLocalMap的get方法
private ThreadLocal.ThreadLocalMap.Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
ThreadLocal.ThreadLocalMap.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;
//开放定址法寻找key对应的entry
while (e != null) {
ThreadLocal<?> k = e.get();
//查到对应的值
if (k == key)
return e;
//键已被回收
if (k == null)
//清理过期的entry
expungeStaleEntry(i);
else
//找下一个地址
i = nextIndex(i, len);
e = tab[i];
}
//桶为空说明值肯定不存在
return null;
}
最后,了解一下map的扩容方法
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();
//key已经被回收,则value置空,有助于垃圾回收
if (k == null) {
e.value = null; // Help the GC
} else {
//重新hash
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
setThreshold(newLen);
size = count;
table = newTab;
}