一文理解ThreadLocal

概述

到底什么是ThreadLocal? 我们先来看看JDK里是怎么说的(JDK1.8)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
This class provides thread-local variables.  These variables differ from
* their normal counterparts in that each thread that accesses one (via its
* {@code get} or {@code set} method) has its own, independently initialized
* copy of the variable. {@code ThreadLocal} instances are typically private
* static fields in classes that wish to associate state with a thread (e.g.,
* a user ID or Transaction ID).
*
* <p>For example, the class below generates unique identifiers local to each
* thread.
* A thread's id is assigned the first time it invokes {@code ThreadId.get()}
* and remains unchanged on subsequent calls.
</pre>
* <p>Each thread holds an implicit reference to its copy of a thread-local
* variable as long as the thread is alive and the {@code ThreadLocal}
* instance is accessible; after a thread goes away, all of its copies of
* thread-local instances are subject to garbage collection (unless other
* references to these copies exist).
谷歌翻译一下
该类提供线程局部变量。 这些变量不同于它们的正常对应物是每个访问一个(通过它或方法)的线程都有自己独立初始化的变量副本。 实例通常是希望将状态与线程相关联的类中的私有静态字段(例如,用户ID或事务ID)。
每个线程都拥有对其本地线程副本的隐式引用只要线程处于活动状态,就可以变量实例可访问; 一个线程消失后,它的所有副本程局部实例受垃圾回收(除非其他存在对这些副本的引用)。

那么到底什么意思呢?
ThreadLocal提供了线程的局部变量,每个线程访问到的ThreadLocal并且往其中set()的变量是属于当前线程的,其他线程访问不到,实现了线程的数据隔离。

内部实现

想要深入了解ThreadLocal,还是先看一下代码吧。

1
2
3
4
5
6
7
8
9
10
11
12
//JDK1.8中ThreadLocal的 set源码
public void set(T value) {
//得到当前线程对象
Thread t = Thread.currentThread();
//获取当前线程对象的ThreadLocalMap
ThreadLocalMap map = getMap(t);
//如果map存在,则t作为key,要存的对象作为value,存到map中
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

看了这段代码 又有了疑问,什么是ThreadLocalMap?点进去看看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static class ThreadLocalMap {

/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;

Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}

原来ThreadLocalMap是ThreadLocal的一个内部类,数据是用Entry存储的
看看getMap方法

1
2
3
4
5
6
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
什么是threadLocals?
threadLocals在Thread类中为线程维护的一个threadlocalMap
ThreadLocal.ThreadLocalMap threadLocals = null;

这就清楚多了,从上面来看,一个Thread维护一个ThreadLocalMap,ThreadLocalMap的set方法的key是ThreadLocal本身,value是要存储的对象
再来看看get方法,有了以上的基础,那么看get方法我相信已经没有难度了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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();
}
// 还是根据当前线程对象从ThreadLocalMap的内部类Entry中取值

总结

  1. 一个Thread维护一个ThreadLocalMap
  2. set方法是通过ThreadLocalMap的内部类Entry来存储
  3. ThreadLocalMap的set方法的key是ThreadLocal本身,value是要存储的对象
  4. ThreadLocal是通过ThreadLocalMap来存储值,本身并不存储值,只是作为一个key

    综上所述,ThreadLocal才能实现线程间的数据隔离