java.lang.ThreadLocal 源码解析
原文发表于:http://blog.csdn.net/qq_27485935 , 大家没事可以去逛逛 (ง •̀_•́)ง
ThreadLocal
概述: 位于 java.lang 包下, 是一个线程内部的数据存储类, 通过它可以在指定线程中存储数据, 数据存储之后, 只有在指定线程中才可以获取到存储的数据。
使用场景: Looper、 ActivityThread
基本用法:
mThreadLocal = new ThreadLocal<Integer>();
mThreadLocal.set(10); // 通过 set 方法设值
mThreadLocal.get(); // 通过 get 方法取值
源码解析
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;
}
我们一步步分析, 首先通过 getMap()
方法获取 Thread 内部 的 threadLocals 对象。 而 ThreadLocalMap 为 ThreadLocal 内部的静态类。
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 Entry[] table;
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();
}
}
源码可见, ThreadLocalMap 通过 Entry 数组来保存键值对的, 并且对键也是弱引用状态。 set 一个数值, 将会取出键(ThreadLocal) 中的 threadLocalHashCode 值, 而 threadLocalHashCode 是唯一的, 所以可以通过它来确定数值在 Entry 数组中的位置。
所以回到刚刚的 ThreadLocal#set
方法。 先从 Thread 类中取出维持线程本地数据的 ThreadLocalMap 类, 若 map 对象不为空, 直接将 ThreadLocal 和数据作为参数 set 到 map 中维持的 table 数组中。 若 map 对象为空, 调用 createMap
方法, 创建 ThreadLocalMap 对象, 设入数据并将该对象设置到 Thread 对象当中。 以下为 createMap
源码。
ThreadLocal#createMap
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
接下来分析 get
方法
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)
return (T)e.value;
}
return setInitialValue();
}
同样, 获取当前线程对象, 并获取维持线程本地数据的 threadLocals, 以 ThreadLocal 对象取出对应数值对象。 如果对象不为空, 就强制转化为泛型 T 对应的类型。 若 map 为空, 或者键值对取出数据对象为空, 就调用 setInitialValue
方法, 以下代码就不解释了。
ThreadLocal#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;
}
从 ThreadLocal 的 set
和 get
方法可以看出, 它们所操作的对象都是当前线程的 threadLocals 对象中的 table 数组, 因此在不同线程中访问同一个 ThreadLocal 的 set
和 get
方法, 他们对 ThreadLocal 所做的读/写操作仅限于各个线程的内部, 获取的值也就不同了。