关于ThreadLocal存取数据的流程分析
一、关于存数据ThreadLocal.set()
ThreadLocal.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);
}
关于ThreadLocal.getMap()方法:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
Thread.getMap()直接返回当前线程的threadlcoals属性
① 首先获取当前线程对象t
② 调用getMap()方法来获得t的threadlocals属性(可见threadlcoals是每个线程都有的属性)
ThreadLocalMap的声明如下:默认值为空
ThreadLocal.ThreadLocalMap threadLocals = null;
③ 判断获得的ThreadLocaMap对象是否为空。
如果不为空,表示不是第一次调用ThreadLocal.set()方法,直接调用ThreadLocalMap.set()方法,设置值。
如果为空,则代表是第一次调用ThreadLocal.set()方法,就调用ThreadLocal.creatTable()方法。
ThreadLocal.creatTable()方法:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
直接new一个ThreadLocalMap对象(这个类是ThreadLocal的一个静态内部类)赋给当前线程对象的threadlocals属性。
new ThreadLocalMap()构造方法的源码:
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//可以看到传入的ThreadLocal对象和firstValue被封装到Entry对象,并放到了table[]中
table[i] = new Entry(firstKey, firstValue);
size = 1; setThreshold(INITIAL_CAPACITY);
}
关于table[]:它是ThreadLocal的静态内部类ThreadMap的一个属性:
private Entry[] table;
二、关于取数据ThreadLocal.get()
ThreadLocal.get()的源码如下:
public T get() {
//获取当前线程的threadlocals属性
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
//如果map不为null,执行getEntry()方法,以this(调用该方法的LocalThread对象)为key,获取对应的Entry对象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
//获取到的Entry对象不是null,获取它的值返回
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//没有获取到map对象,或是当前的ThreadLocal对象在Entry[](ThreadLocalMap的table属性)没有与key和它相同,就调用初始化值方法。
return setInitialValue();
}
三、设计理由
可以看到上面的流程比较复杂,为什么不能直接向Thread类中的ThreadLocalMap对象存取数据呢?这是不能实现的,看一下threadLocals的声明:
(缺省:包级访问)ThreadLocal.ThreadLocalMap threadLocals = null;
变量threadLocals是包级访问,所以不能从外部直接访问该变量,只有同名的包中的类可以访问threadLocals变量,而ThreadLocal和Thread恰好在一个包中,可以访问。(其实这样设计也是为了只能让ThreadLocal来访问,ThreadLocalMap的一个内部类它的一个实例也是Thread的一个属性,外部想要操作这个这个属性是行不通的,只有通过ThreadLocal来操作这个属性,不如说为了只让ThreadLocal来操作才是设计包级访问的原因,只能让ThreadLocal来操作也是为了确保这是线程私有的数据,否则只是普通的设计一个Map,那么其他的线程也可以获取并修改数据)。
同一个ThreadLocal可以存在于多个Thread对象的theadLocals属性中作为key值,但在不同的线程中通过其对象来获取的值有各个线程所决定。这是因为么个线程对象都有一个ThreadLocalMap对象。这样其他线程即使获取了ThreadLocal对象也只能修改自己的数据。注意,线程的Map中的key必须是不同ThreadLocal对象,否则会覆盖。
ThreadLocal存取数据的操作都要先获取当前对象 ,就是为了确保数据的线程私有性,其他线程不能操作除自己以外的其他线程的数据,这也是ThreadLocal的初衷。将ThreadLocalMap设为ThreadLocal的内部类也是为了对于ThreadLocalMap的操作只对ThreadLocal可见(可以看到ThreadLocalMap的方法基本全是private的,只对外部类ThreadLocal可见)。