ThreadLocal部分原始碼分析

BLLBL發表於2021-10-24

結構演進

早起JDK版本中,ThreadLocal內部結構是一個Map,執行緒為key,執行緒在“執行緒本地變數”中繫結的值為Value。每一個ThreadLocal例項擁有一個Map例項。(Key是執行緒,Value是值)

JDK8中,ThreadLocal內部結構發生了演進,雖然還是Map,但是擁有者變成了Thread例項,每一個Thread例項擁有一個Map例項。Map中的key變為ThreadLocal例項。(Key是ThreadLocal,Value是值)

新版ThreadLocalMap如圖:

image

每一個執行緒在獲取本地值時,都會將ThreadLocal例項作為Key從自己擁有的ThreadLocalMap中獲取值,別的執行緒無法訪問自己的ThreadLocalMap例項,達到相互隔離的目的。

新版優勢(減少了記憶體消耗)

(1)ThreadLocalMap儲存的鍵值對數量減少。早期版本的數量與執行緒個數強關聯;新版的Key是ThreadLocal例項,會比執行緒數少。

(2)早期版本ThreadLocalMap擁有者為ThreadLocal,線上程銷燬後,ThreadLocalMap仍然存在;新版的ThreadLocalMap擁有者為Thread,Thread例項銷燬後,ThreadLocalMap也會隨之銷燬,減少記憶體消耗。

Entry的Key需要使用弱引用

為什麼不直接使用ThreadLocal例項作為Key呢?

public void funcA() {
    ThreadLocal local = new ThreadLocal<Integer>();
    local.set(100);
    local.get();
}

當執行緒執行funA方法時,先新建一個local例項,這是強引用,呼叫set方法後會在內部新建Entry例項,Key是弱引用包裝指向的local例項。當執行緒執行完A方法後,方法棧幀被銷燬,強引用local的值也沒有了,但此時執行緒的ThreadLocalMap對應Entry的Key引用還指向的ThreadLocal例項都不能被GC回收,這將造成記憶體洩漏問題。

弱引用:物件只能生存到下一次垃圾回收之前。

由於ThreadLocalMap中Key是弱引用,下次GC發生時,可以把那些沒有強引用指向的ThreadLocal回收。並且key被回收後,其Entry的key值變為null。後續ThreadLocal的get、set方法被呼叫時,就會清除這些Key為null的Entry,完成記憶體釋放。

使用state final修飾ThreadLocal物件

ThreadLocal例項作為ThreadLocalMap的Key,針對一個執行緒內的所有操作是共享的,使用static修飾ThreadLocal節約空間。

Re

《Java高併發核心程式設計》

相關文章