引言
ThreadLocal 可以在每个线程中存取数据,并且不同的线程中的数据互不影响。使用在数据以线程为作用域并且不同的线程拥有不用的数据副本,或者是复杂的参数传递时(参数在同一线程中的不同类中传递)。
在分析消息机制源码的时候,涉及到了 ThreadLocal,使用是在 Looper 类中通过 ThreadLocal<Looper> 对象的 set 方法和 get 方法存取当前线程的 Looper 对象
我们发现 ThreadLocal 对象是一个静态的对象,说明每个线程都可以通过该对象来存取当前线程对应的 Looper ,说明 ThreadLooper 中存放的数据确实是以线程为作用域的,源码接着看
源码分析
看源码之前,先大体的说一下工作的原理。每个线程类 Thread 中都保存了一个 ThreadLocalMap ,可以理解为就是一个 Map,Map 的 key 就是这个 Thread 中所有使用到的 ThreadLocal 对象,Map 的 value 是 Thread 中使用该 ThreadLocal 存储的数据的值。在使用时通过 ThreadLocal 即可存取对应的 value。
可以这么理解。但是 ThreadlocalMap 并不是一个 Map,其内部通过一个 Entry 类型的数组 Entry[] 来存储 ThreadLocal 和其存储的值,Entry 中保存了 value 和 ThreadLocal,Entry 是继承了 WeakReference ,也就是说 Entry[] 是以软弱引用来保存 Entry 的,并且数组中的索引是通过 ThreadLocal 的 hashCode 计算的值,这样根据 ThreadLocal 的 hashCode 也就有了与 Entry[] 中每个索引位置的值之间的关系。
ThreadLocalMap 存在的意义不仅仅是保存 Entry[] 数组,也为该数组中数据的存取提供了更多计算方法
// Entry 中只保存了 value,super 方法中会保存 ThreadLocal
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
set() 方法用于数据的存储
// ThreadLocal 的 set() 方法
public void set(T value) {
Thread t = Thread.currentThread(); // 获取当前线程
ThreadLocalMap map = getMap(t); // 从线程中取出 ThreadLocalMap
if (map != null)
map.set(this, value); // 如果 ThreadLocalMap 不为 null ,则使用 ThreadLocalMap 存储
else
createMap(t, value); // 如果 ThreadLocalMap 为 null,则为该线程初始化 ThreadLocalMap ,并将数据存储
}
// ThreadLocal 的 getMap() 方法
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// ThreadLocalMap 的 set() 方法
private void set(ThreadLocal key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1); // 根据 ThreadLocal 计算在 Entry[] 数组中的索引值
// 如果该索引位置有值,则判断 ThreadLocal 是否匹配,不匹配则遍历到下一有值位置
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get();
// 如果有值则判断当前位置的 ThreadLocal 和需要插入的 ThreadLocal 是否相同,如果相同则直接修改 value 为新值
if (k == key) {
e.value = value;
return;
}
// 如果对应位置 ThreadLocal 为空,则取代旧的 Entry ,重新赋值新的 Entry
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
// 如果该索引位置没有值,则直接赋值为新值
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
// ThreadLocal 的 createMap() 方法
void createMap(Thread t, T firstValue) { // Thread 的 ThreadLocalMap 为 null 时,为 Thread 创建新的 ThreadLocal 并将 ThreadLocal 及 Value 存储
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// ThreadLocalMap 的构造方法
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY]; // 初始化 Entry[]
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); // 根据 ThreadLocal 计算索引值
table[i] = new Entry(firstKey, firstValue); 为该索引位置赋值
size = 1;
setThreshold(INITIAL_CAPACITY);
}
通过在 Thread 中获取 ThreadLocalMap ,再根据 TheadLocal 和 Value 将数据存储到了 Entry[] 数组中,具体的存储过程看注释。
有一个关键点,存储的时候,如果 ThreadLocal 对应的位置有值,则会查看向下一个位置,如果该位置还是有值则继续向下一位置,直到没有值的位置插入该 Entry
存:判断 ThreadLocalMap 是否为空
为空:创建 ThreadLocalMap 并为对应位置赋值
-
不为空:判断当前位置是否有值 Entry
- 无值:为当前位置赋值为新的 Entry
- 有值:判断 ThreadLocal 是否对应
- 对应:修改旧 Value 值
- 不对应:向下一有值索引位置判断,直到 ThreadLocal 对应(修改原 Value 值) 或遇到该索引对应值无 ThreadLocal 则为该位置赋值新 Entry,或遇到无值索引时为该位置赋值新 Entry
get() 方法源码解析
// ThreadLocal 的 get() 方法
public T get() {
Thread t = Thread.currentThread(); // 获取当前线程 Thread
ThreadLocalMap map = getMap(t); // 获取该线程中的 ThreadLocalMap
if (map != null) { // 如果 ThreadLocalMap 不为空
ThreadLocalMap.Entry e = map.getEntry(this); 调用 ThreadLocalMap 的 getEntry 方法获取 Entry 对象
if (e != null) // Entry 不为空则将 Entry 中存储的 Value 返回
return (T)e.value;
}
return setInitialValue(); // 线程的 ThreadLocalMap 为空或者 ThreadLocalMap 中无对应 Entry 情况
}
// ThreadLocalMap 的 getEntry() 方法
private Entry getEntry(ThreadLocal key) {
int i = key.threadLocalHashCode & (table.length - 1); // 计算在 Entry[] 中的索引
Entry e = table[i];
if (e != null && e.get() == key) // 如果 Entry 不为 null 且当前位置对应的 ThreadLocal 为该 ThreadLocal 则返回 Entry
return e;
else
return getEntryAfterMiss(key, i, e); // 该位置不对应则向下一位置遍历查询
}
// ThreadLocalMap 的 getEntryAfterMiss() 方法
// Entry 为 null 或 ThreadLocal 不对应则向下一个位置遍历,直到 Entry 的 ThreadLocal 和当前 ThreadLocal 对应或遍历结束,如果遍历结束还是 null 则返回 null
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
// ThreadLocalMap 的 setInitialValue() 方发法,线程的 ThreadLocalMap 为空或者 ThreadLocalMap 中无对应 Entry 情况
private T setInitialValue() {
T value = initialValue(); // 该方法默认返回 null,可重新该方法修改默认值
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value); // ThreadLocalMap 不为空则为该位置创建值为默认值的 Entry
else
createMap(t, value); // ThreadLocalMap 为空时,则为该线程创建 ThreadLocalMap 并未该位置创建值为默认值的 Entry
return value; // 将默认值返回
}
get() 方法主要为从 ThreadLocalMap 中取出对应的值,在该位置没有值或当前线程无对应 ThreadLocalMap 情况下,会返回默认的值 null
取,判断 ThreadLocalMap 是否为空
-
为空
- 创建新 ThreadLocalMap 并将该位置赋值 Value 为默认值的 新 Entry ,并返回
-
不为空
- 根据 ThreadLocal 计算索引,判断该索引位置是否有值
- 无值:为该索引位置赋值 Value 为默认值的 新 Entry ,并返回
- 有值:判断 ThreadLocal 是否匹配
- 匹配:返回对应位置 Value
- 不匹配:遍历下一位置直到 ThreadLocal 匹配时返回该 Entry,或该位置的 Entry 的 ThreadLocal 为空时删除该位置之后的所有空条目,继续向下一位置遍历,遍历结束如果还是没有对应 ThreadLocal 则返回 null
- 根据 ThreadLocal 计算索引,判断该索引位置是否有值