概述
ThreadLocal可以看做是线程的局部变量,用于存储线程内部的数据的容器,其内部主要通过ThreadLocalMap类来实现数据的存储与访问。
常见用法
ThreadLocal t = new ThreadLocal<>();
t.set(1);
t.get();
源码分析
ThreadLocal相关的源码逻辑比较简单。
ThreadLocalMap
- ThreadLocalMap内部采用一个继承自WeakReference的Entry数组来存储数据,数组长度为2的幂;
- 每个Entry存储一个ThreadLocal对象和相应的值;
- 数据写入:通过ThreadLocal的hash值获取在Entry数组中的位置,若该位置为null,则进行初始化;若该位置已经有值且key不为当前ThreadLocal,则循环向后推移一位,直到数据写入;
- 数据读取:根据写入的规则,如果该hash位置已经有值且不为当前ThreadLocal,则循环向后推移一位,直到数据写入或读出;
- 数组扩容等情况暂未研究。
构造方法
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);
}
set()
private void set(ThreadLocal> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
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();
}
getEntry()
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);
}
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
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
remove()
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;
}
}
}
ThreadLocal
- ThreadLocal相对比较简单,基本就是从ThreadLocalMap中存取数据;
- 通过Thread.currentThread()获取当前线程,然后读取当前线程的threadLocals对象,接下来就是ThreadLocalMap的相关操作了;
- 数据写入:若map不为null,则正常调用map的set(ThreadLocal> key, Object value)方法赋值;否则初始化ThreadLocalMap并赋值;
- 数据读取:若map不为null,则正常调用map的getEntry(ThreadLocal> key)方法读取;否则初始化ThreadLocalMap并赋null值,然后返回null;
- 数据删除:若map不为null,则正常读取;否则不处理;
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);
}
get()
public T get() {
Thread t = Thread.currentThread();
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();
}
withInitial()
public static ThreadLocal withInitial(Supplier extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}
此方法主要通过Supplier来对ThreadLocal值进行初始化。
用法:
ThreadLocal t = ThreadLocal.withInitial(() -> 100);
Thread/ThreadLocal/ThreadLocalMap的关系
简单的归纳了下三者之间的关系:
- 一个Thread可有多个ThreadLocal;
- 一个ThreadLocal也可以供多个Thread使用;
- ThreadLocalMap为Thread的一个全局变量;
- ThreadLocal为ThreadLocalMap的key;
-
可以将ThreadLocal作为Thread处理ThreadLocalMap的媒介。