官方描述: 該類提供了線程局部 (thread-local) 變量。這些變量不一樣於它們的普通對應物,由於訪問某個變量(經過其 get 或 set 方法)的每一個線程都有本身的局部變量,它獨立於變量的初始化副本。ThreadLocal 實例一般是類中的 private static 字段,它們但願將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯。java
本身的理解 ThreadLocal線程本地變量。爲每一個線程建立一個變量副本。 TODO面試
public void set(T value) {
Thread t = Thread.currentThread();
//重要:根據線程獲取Map
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
複製代碼
根據當前線程獲取Map,若是Map爲空,則createMap().數據庫
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;//返回一個**線程綁定**的ThreadLocalMap
}
複製代碼
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
複製代碼
能夠看出,其實Thread自己就內置了一個ThreadLocalMap。編程
那麼接下來的重點就是看ThreadLocalMap如何初始化了。數組
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化一個長度=16的對象數組。Entry extends WeakReference
table = new Entry[INITIAL_CAPACITY];
//肯定Hash值
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//設值
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
複製代碼
(重要!因此用截圖) bash
拓展:併發
麻煩點:若是Hash衝突了怎麼辦呢?函數
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解決Hash衝突的方式就是簡單的步長加1或減1,尋找下一個相鄰的位置。ui
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();
}
複製代碼
數據庫鏈接、Session管理this
參考