ThreadLocal,即线程局部变量,其实就是一个容器,每个线程都可通过其set方法保存一份数据,并且在get时只会获取自己线程的数据,是常用的实现线程安全的方式。
自己用过许多次了,但没有细究其原理,今天大概看了一下源码,算是明白了一点点0.0
直接看ThreadLocal的get方法
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();
}
首先就找到当前线程对象,然后根据当前线程对象调用getMap方法。果然还是要用map来保存数据的,只不过这个map并不是我们常用的HashMap之类,而是ThreadLocal的一个静态内部类:ThreadLocalMap。
getMap方法最终返回的是线程对象的一个ThreadLocalMap属性
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
Thead类属性:
ThreadLocal.ThreadLocalMap threadLocals = null;
也就是说这个map是被Thread对象直接引用的,现在大概明白了为什么不同线程能够获取不同的数据,原因就是每个线程都有一份自己的ThreadLocalMap
接下来就看看ThreadLocalMap大致代码
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;
private Entry[] table;
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);
}
可以看到ThreadLocalMap中的Entry继承了WeakReference,意思就是保存的是以ThreadLocal为键(该键为弱引用,这里也是内存泄漏的隐患地),Object为值的数据,用一个Entry数组来保存多组数据,初始大小为16
但是为何会以ThreadLocal为键呢?其实不难理解,每个线程对象有自己的ThreadLocalMap属性,当一个线程中调用ThreadLocal对象.get()方法时,会根据当前线程对象找到其引用的ThreadLocalMap对象,然后调用map的getEntry()方法获取对应数据。
但我们的代码中不可能永远只有一个ThreadLocal对象,我们完全有可能定义一个ThreadLocal< String >来保存线程对应的字符串,定义一个ThreadLocal< Integer >来保存线程对应的数值…多个ThreadLocal对象调用get()时均找到当前线程的ThreadLocalMap对象,他们如何在Entry数组中找到自己想要的数据呢?这时就应以ThreadLocal对象作为区分,取出不同的值
看到这里其实懂了大概原理了,具体以后慢慢看吧。。。