Handler---4部曲---1. 總體流程
Handler---4部曲---2. ThreadLocal 存儲主流程
Handler---4部曲---3. MessageQueue隊列
Handler---4部曲---4.細節補充
前言
看Handler源碼, 每次一說到ThreadLocal的保存, 幾乎都說可以理解成key就是當前線程, value就是對應的值. 實際上真的是這樣嗎?
主要了解整個存儲的流程,細節方面不做講解.
先上圖
一. 先記住
每個ThreadLocal對象中,threadLocalHashCode 的值是固定的
//ThreadLocal類中
final int threadLocalHashCode = nextHashCode();
注意:
- table數組的角標 i 和 ThreadLocal.threadLocalHashCode 有關.
- key當前ThreadLocal對象
- value 就是存入的值
二. ThreadLocal.set() 方法
public void set(T value) {
Thread t = Thread.currentThread();
//從當前線程中, 獲取成員變量 threadLocals
ThreadLocalMap map = getMap(t);
if (map != null)
//保存數據
map.set(this, value);
else
// 首次進入,創建一個ThreadLocalMap,並賦值給當前線程
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
步驟:
- 從當前線程取出成員變量:ThreadLocalMap threadLocals;
- threadLocals爲空, 創建ThreadLocalMap, 並賦值.
- threadLocals不爲空,直接設置值
- 每個線程保存的本質, 就是在當前線程中的ThreadLocalMap對象中進行存儲.
三. 下面看看 new ThreadLocalMap() 和 ThreadLocalMap.set() 都做了些什麼?
3.1 ThreadLocalMap構造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化table數組, 數組裏面是沒有值的
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//保存對應的值
table[i] = new Entry(firstKey, firstValue);
}
步驟:
- 初始化table數組---->沒有賦值
- tab[i] 保存值
3.2 ThreadLocalMap中的set方法
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
//tab的位置
int i = key.threadLocalHashCode & (len-1);
//Entry e = tab[i] --->當前 ThreadLocal 對應的值
// 如果tab[i] 有值, 證明之前保存過
for (Entry e = tab[i];
e != null;
//這裏更新了i, i = i+1
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//同一個ThreadLocal, 直接覆蓋
if (k == key) {
e.value = value;
return;
}
}
//如果 tab[i] == null, 直接保存
tab[i] = new Entry(key, value);
int sz = ++size;
//判斷table數組是否需要擴容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
步驟:
- 查看tab[i]是否有值?
- 如果有值,並且key也相同, 覆蓋原來的值
- 如果沒有值,直接賦值.
- 判斷table數組 是否擴容.
結論:
1. 一個線程Thread, 只有一個ThreadLocalMap成員變量, 可以有多個ThreadLocal對象
2. 同一個線程,多個ThreadLocal時, 存儲的值在同一個Thread.ThreadLocalMap裏面的 table數組. 類型Entry[]
3. 不同線程, 同一個ThreadLocal時, 存儲在不同的Thread對象中. 1個Thread 對應 1個 ThreadLocalMap