首先要带着问题去分析,
- 新城变量的副本存储在那里?
- 变量副本是怎么从共享的那个变量赋值出来的?源码中的threadlocal是什么时候初始化的
ThreadLocal是如何实现多个线程之间每一个线程都持有该线程都变量副本?如何共享变量的
ThreadLocal提供了set和get方法用来访问当前线程的线程局部变量
public class ThreadLocal<T> {
...
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value
* returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
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;
}
//如果当前线程中的ThreadLocalMap == null就进行初始化的操作
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;
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
...
}
- 首先看ThreadLocal类的成员函数get方法的注解,返回当前线程的变量副本,如果当前线程不存在变量副本,那么就初始化一个容量为0的变量副本
- 分析ThreadLocal类的成员函数get可知,首先根据当前线程去获取它的成员变量threadLocals,他的类型为ThreadLocalMap是ThreadLocal的一个内部类。接下来赋值给局部变量map,紧接着执行
map != null
,如果是首次执行,此时map==null,那么接下来就会调用ThreadLocal类的成员函数setInitialValue去进行初始化的操作,跟踪代码最终执行到了成员函数createMap,也就是创建了一个ThreadLocalMap的实例并赋值给了当前线程t的成员变量threadLocals;如果不是第一次的操作,那么此时map != null
是成立的,那么此时就可以根据ThreadLocal实例直接获取到存储到变量副本
ThreadLocalMap解析
public class ThreadLocal<T> {
...
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
...
private static final int INITIAL_CAPACITY = 16;
...
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是ThreadLocal的一个内部类,然后还有一个内部类Entry,ThreadLocalMap构造初始化时候,创建了一个size = 16的Entry数据,并且把当前线程持有的ThreadLocal对象作为key传入,当前线程的便利那个副本作为value传入,并存入数据中去,这样子就可以关联起来ThreadLocal与当前线程的变量副本
- 总结下来其实每个线程的变量副本是存放在自己的成员变量threadLocals中,他的类型是ThrealLocalMap,然后通过ThreadLocal去访问对应的变量副本
变量副本是什么时候创建出来的呢?
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- 当前线程中的threadLocalMap是null的时候,会调用createMap创建一个新的map,并且赋值给当前的线程的成员变量threadLocals。
总结如下:
1、在代码中声明的ThreadLocal对象,实际上只有一个。
2、在每个线程中,都维护了一个threadlocals对象,在没有ThreadLocal变量的时候是null的。一旦在ThreadLocal的createMap函数中初始化之后,这个threadlocals就初始化了。以后每次那个ThreadLocal对象想要访问变量的时候,比如set函数和get函数,都是先通过getMap(t)函数,先将线程的map取出,然后再从这个在线程(Thread)中维护的map中取出数据【以当前threadlocal作为参数】。