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節約空間。

應用場景

場景一:線程隔離

這個的典型應用就是“數據庫連接獨享”。下面的代碼來自Hibernate,代碼通過ThreadLoacl進行數據庫連接(Session)的線程本地化存儲。

private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() {
    Session s = (Session) threadSession.get();
    if(s == null) {
        s = getSession();
        threadSession.set(s);
    }
    return s;
}

一個Session代表了一個數據庫連接。通過以上代碼可以看出,這個Session相當於線程的私有變量,不是所有線程共用的,其他線程是獲取不到這個Session的。

一般來說,完成數據庫操作之后程序會將Session關閉,節省資源。如果Session為共享的方式,如果某個線程將Session關閉,其他線程在操作Session時就會報錯。所以通過ThreadLocal簡單實現了數據庫連接的安全使用。

Re

《Java高並發核心編程》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM