最近在學習ThreadLocal相關知識,進行了下記錄。
一:ThreadLocal是什麼?
ThreadLocal是服務于Thread的一種本地私有資料機制,threadlocalvariable(線程局部變量), 即為每一個使用該變量的線程都提供一個變量值的副本,與使用的線程綁定,每一個線程都可以獨立地改變自己的副本,不會産生沖突。 其在Thread處于活躍狀态,并且threadLocal對象可用的情況下,就可以在其中存放資料。線上程被銷毀之後,則ThreadLocal中使用的資源和記憶體同時也被回收。本質上,JVM通過ThreadLocal為多線程情況下,進行資源的通路提供了一種隔離機制,簡化了程式設計的複雜度。
二:ThreadLocal的實作原理是什麼?
每一個線程都有一個對應的Thread對象,而Thread類有一個成員變量,它是一個Map集合,這個Map集合的key就是ThreadLocal的引用,而value就是目前線程在key所對應的ThreadLocal中存儲的值。當某個線程需要擷取存儲在ThreadLocal變量中的值時,ThreadLocal底層會擷取目前線程的Thread對象中的Map集合,然後以ThreadLocal作為key,從Map集合中查找value值。這就是ThreadLocal實作線程獨立的原理。也就是說,ThreadLocal能夠做到線程獨立,是因為值并不存在ThreadLocal中,而是存儲線上程對象中。下面我們根據ThreadLocal中兩個最重要的方法來确認這一點。
三:接下來就先了解下ThreadLocal的相關屬性
3.1 threadLocalHashCode:該字段的含義是:
線程擷取threadLocal.get()時, 如果第一次在某個 threadLocal對象上get時,會給目前線程配置設定一個value, 如果這個value和目前的threadLocal對象 被包裝成一個entry 其中 key 是 threadLocal對象,value是threadLocal對象給目前線程生成的value.
這個entry 存放到目前線程 threadLocals 這個map 的哪個桶位?
于目前threadLocal對象的threadLocalHashCode有關系
使用threadLocalHashCode & (table.length-1)得到的位置,就是目前entry需要存放的位置
3.2 nextHashCode:建立threadLocal對象時,每建立一個threadLocal對象 就會使用nextHashCode 配置設定一個hash值給這個對象
private static AtomicInteger nextHashCode =
new AtomicInteger();
3.3 HASH_INCREMENT: 每建立一個threadLocal對象,這個ThreadLocal.nextHashCode 這個值就會增長 0x61c88647。這個值就是 斐波那契數。
3.4 nextHashCode()方法: 建立新的ThreadLocal對象時,會給目前對象配置設定一個hash.使用的就是這個方法
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
3.5 initialValue()方法:預設傳回null,一般需要重寫
protected T initialValue() {
return null;
}
3.6 接下來就是比較重要的三個方法之一get()方法:get方法的作用是:
傳回目前線程于目前ThreadLocal對象相關聯的 線程局部變量,這個變量隻有目前線程能通路到。
如果目前線程沒有配置設定,則給目前線程去配置設定(使用initialValue()方法)
在get()方法中, 先擷取目前線程,在通過getMap()方法擷取threadLocals map引用 。
如果條件成立,說明目前線程已經擁有了自己的ThreadLocalMap對象, 同時entry如有有值,表示目前線程已經初始化過了,于目前線程threadLocal對象關聯的 線程局部變量,并傳回線程的value值
如果map活着entry為null,則會進行初始化.
// 傳回目前線程于目前ThreadLocal對象相關聯的 線程局部變量,這個變量隻有目前線程能通路到。
// 如果目前線程沒有配置設定,則給目前線程去配置設定(使用initialValue()方法)
public T get() {
// 擷取目前線程
Thread t = Thread.currentThread();
// 擷取目前線程Thread對象的 threadLocals map 引用
ThreadLocalMap map = getMap(t);
// 如果條件成立,說明目前線程已經擁有自己的 ThreadLocalMap 對象了
if (map != null) {
// 可以:目前threadLocal對象
// 調用map.getEntry()方法,擷取threadLocalMap中 該threadLocal 關聯的 entry
ThreadLocalMap.Entry e = map.getEntry(this);
// 條件成立,目前線程初始化過 與目前線程threadLocal對象相關聯的 線程局部變量
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
// 傳回value值
return result;
}
}
// 執行到這裡,map是null 或者 entry是 null
// 初始化目前線程與目前threadLocal對象 相關聯的value
// 且目前線程如果沒有threadLocalMap的話,還會初始化建立map
return setInitialValue();
}
在這個方法中,有兩個小方法;getMap()方法和setInitialValue()方法
3.7 getMap()方法: 主要作用是傳回目前線程的 threadLocals
ThreadLocalMap getMap(Thread t) {
// 傳回目前線程的 threadLocals
return t.threadLocals;
}
3.8setInitialValue()方法:進行目前線程的初始化
先調用initalValue()方法進行初始化,擷取目前線程。進行調用getMap()方法
如果map有值,會儲存目前threadLocal與目前線程相關聯的value。這個map的key 就是目前線程的threadLocal對象.value就是 線程與目前threadLocal相關的局部變量
如果map為null ,會進行建立。傳入的k為目前線程和目前線程的value值
最後傳回線程與目前threadLocal的局部變量
private T setInitialValue() {
// 調用目前ThreadLocal對象的initialValue方法
// value 就是目前ThreadLocal對象與目前線程生成的線程的局部變量
T value = initialValue();
//擷取目前線程
Thread t = Thread.currentThread();
// 擷取目前線程内部的threadLocals threadLocalMap對象
ThreadLocalMap map = getMap(t);
// 條件成立,說明目前線程内部初始化過threadLocalMap對象了(線程的threadLocals 隻會初始化一次)
if (map != null)
// 儲存目前threadLocal與目前線程相關聯的 value
// key:目前threadLocal對象
// value:線程于目前threadLocal相關的局部變量
map.set(this, value);
else
// 不成立的話,說明目前線程内部還未初始化
// 參數一:目前線程; 參數二:目前線程的value值
createMap(t, value);
//傳回線程于目前threadLocal的局部變量
return value;
}
這這個方法中,有個新的方法createMap():方法
3.9.createMap()方法:進行建立一個新的threadLocalMap
void createMap(Thread t, T firstValue) {
// 傳遞t 的意義就是 要通路 目前這個線程 t.threadLocals字段,給這個字段初始化
// new ThreadLocalMap(this,firstValue) 建立一個ThreadLocalMap對象
// 初始化 key: this目前threadLocal對象
// value: 線程于目前threadLocal相關的局部變量
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
3.11接下來一個方法,也是ThreadLocal中最重要的三個方法之一 set()方法
主要作用是:修改目前線程與目前threadLocal對象相關的局部變量
通過先擷取目前線程,再擷取ThreadLocalMap ,判斷map是否存在。如果map不為null,表示目前線程已經初始化了,隻需要map.set即可,進行重寫或者添加。
如果map為null,說明還未建立,需要調用createMap()進行初始化的建立
// 修改目前線程于目前threadLocal對象相關聯的 線程局部變量
public void set(T value) {
// 擷取目前線程
Thread t = Thread.currentThread();
// 擷取目前線程threadLocalMap對象
ThreadLocalMap map = getMap(t);
// 條件成立, 說明目前線程的threadLocal已經初始化過了
if (map != null)
// 調用threadLocalMap.set方法,進行重寫或者添加
map.set(this, value);
else
// 執行到這,說明的哪個區線程還未建立
createMap(t, value);
}
3.12第三個重要的方法就是remove()方法: 顧名思義就是移除
移除目前線程于目前threadLocal對象相關聯的 線程局部變量
擷取目前的線程的map,如果存在的話,就進行remove移除
// 移除目前線程于目前threadLocal對象相關聯的 線程局部變量
public void remove() {
// 擷取目前線程的map
ThreadLocalMap m = getMap(Thread.currentThread());
// 如果成立,直接remove
if (m != null)
m.remove(this);
}