ThreadLocal部分源碼分析

結構演進

早起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高並發核心編程》

Tags: