首先,ThreadLocal 并不是一个 Thread,这个类提供了线程局部变量,这些变量不同于它们的普通对应物,因为访问某个变量的每个线程都有自己的局部变量,它独立于变量的初始化副本。
ThreadLocal 是如何做到为每一线程维护变量的副本的呢?下面通过源码 (jdk1.7 版本) 来阐述 ThreadLocal 的基本原理。
在具体分析之前,先做几点说明:
1:在 TreadLocal 中有一个静态内部类 ThreadLocalMap
- static class ThreadLocalMap {
- static class Entry extends WeakReference {
- /** The value associated with this ThreadLocal. */
- Object value;
- Entry(ThreadLocal k, Object v) {
- super(k);
- value = v;
- }
- }...
- }
2:ThreadLocal 中又定义一个键值对 Entry,它用 ThreadLocal 作为键值。我们看到在 Thread 类中有一个 ThreadLocalMap 的类型的变量叫做 threadLocals。
下面具体分析一下 ThreadLocal 的两个关键函数 get() 和 set():
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null) return (T) e.value;
- }
- return setInitialValue();
- }
在 get 函数中,首先获取到当前的线程 t,再根据 t 获取 ThreadLocalMap。下面试 getMap() 函数:
- ThreadLocalMap getMap(Thread t) {
- return t.threadLocals;
- }
可以看到,该函数返回就是我们上面提到的每个线程都有的 ThreadLocalMap 类型变量 threadLocals。
如果 map 不为空,则根据 map.getEntry(this) 获取 Entry 键值对。注意:这里的 this 指的是当前的 ThreadLocal 对象,一个 Thread 可能对应不止一个 ThreadLocal,想要知道具体是 Thread 对应的哪个 ThreadLocal,就要在 Thread 中维护一个 ThreadLocalMap,以 ThreadLocal 为键,就可以找到 Thread 在某个 ThreadLocal 里对应的本地数据。获取到 Entry 后,我们就可以拿到保存在 Entry 里面的 value 值了。
- 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);
- }
如果 map 为空,则调用 setInitialValue() 函数进行初始化。并返回 initialValue 函数返回的值,不覆写 initialValue 的情况下,返回的是 null。
- 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;
- }
- public void set(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) map.set(this, value);
- else createMap(t, value);
- }
set 函数同样是先获取 ThreadLocalMap 类型的变量 map。
如果 map 不为空,则:
- 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();
- }
如果 map 为空,则
- void createMap(Thread t, T firstValue) {
- t.threadLocals = new ThreadLocalMap(this, firstValue);
- }
ThreadLocal 是通过下面的方式来实现为每一个线程维护变量的副本的:
在 ThreadLocal 类中定义了一个 ThreadLocalMap,每一个 Thread 都有一个 ThreadLocalMap 类型的变量 threadLocals,就是用 threadLocals 来存储每一个线程的变量副本,threadLocals 内部有一个 Entry 数组,我们根据键值线程对象,来找到对应线程的变量副本。
来源: http://www.cnblogs.com/xujian2014/p/5777849.html