TheadLocal
是一个线程内部的数据存储类,通常用于存储以线程为作用域的数据变量,避免产生多线程的同步问题。
记得上学时候也写过相关源码的分析文章,但今天翻看Java 8中的ThreadLocal
类时发现它被重构了,因此也重读一下相关源码实现。
ThreadLocal
是一个泛型类,定义如下:
public class ThreadLocal<T>
作为存储类,我们抓住主要矛盾从set
和get
方法开始分析。
ThreadLocal
存数据 —— set方法
public void set(T value) {
//获取到当前线程
Thread t = Thread.currentThread();
//根据当前线程获取ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null) // map对象不为空则存入对象
map.set(this, value);
else // 否则创建ThreadLocalMap
createMap(t, value);
}
可以看到,set方法向一个ThreadLocalMap
对象中存入数据,ThreadLocalMap
是ThreadLocal
的静态内部类,具体实现我们随后进行分析。我们现在只需知道它被用来存储数据(像HashMap那样):key是线程对应的threadlocal对象,value是要存入的数据对象。
作者的叨叨
:其实看到这里机智点的小伙伴可以尝试猜测整个ThreadLocal
类的实现原理。
先别管这样的猜测是不是正确,这不重要。其实我们看源码的时候要养成主动思考的习惯,假如让你来设计或者实现这样的方案,你会怎么设计?然后再与实际源码对比一下,并且关注一下源码的细节。
下面我们接着看一下如何获取之前存入的数据对象。
ThreadLocal
取数据 —— get方法
public T get() {
//获取到当前线程
Thread t = Thread.currentThread();
//获取ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null) {
//获取ThreadLocalMap中的Entry对象并拿到Value
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果之前未创建过ThreadLocalMap,则返回null
return setInitialValue();
}
从get方法中我们可以看到,如果当前线程之前向ThreadLocalMap
存入过数据对象,则根据线程实例获取到存入的数据对象。
看完了存取两个动作,让我们揭开ThreadLocalMap
的面纱,看看ThreadLocal
是如何存储数据对象的。
ThreadLocal
储数据 —— ThreadLocalMap
从上面的set和get方法中都可以看到,ThreadLocal一直在对ThreadLocalMap进行操作。我们来搞清楚Thread,ThreadLocal以及ThreadLocalMap三者之间的关系。
在Thread类中可以看到,每个Thread对象中都持有一个ThreadLocalMap
成员变量:
ThreadLocal.ThreadLocalMap threadLocals = null;
在上面的set方法中我们看到了对ThreadLocalMap的创建:
void createMap(Thread t, T firstValue) {
//创建一个ThreadLocalMap对象赋值给当前线程的成员变量threadLocals
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
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);
}
从构造函数中我们可以看到ThreadLocalMap中维护了一个Entry
对象的数组table:
private Entry[] table;
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry
对象是一种类似<ThreadLocal,Object>键值对的结构,每个ThreadLocal对象都对应了各自的数据对象。现在Thread,ThreadLocal以及ThreadLocalMap三者之间的关系就清楚了:
通过ThreadLocalMap和Thread的一一对应关系实现了线程作用域中的数据对象存取。