Java 200+ 面試題補充 ThreadLocal 模組

老王_Java中文社群發表於2019-03-06

本文是前文《Java 最常見的 200+ 面試題》的第一個補充模組。

讓我們每天都有進步,老王帶你打造最全的 Java 面試清單,認真把一件事做到極致。

1.ThreadLocal 是什麼?

ThreadLocal 是一個本地執行緒副本變數工具類。主要用於將私有執行緒和該執行緒存放的副本物件做一個對映,各個執行緒之間的變數互不干擾,在高併發場景下,可以實現無狀態的呼叫,適用於各個執行緒不共享變數值的操作。

2.ThreadLocal 工作原理是什麼?

ThreadLocal 原理:每個執行緒的內部都維護了一個 ThreadLocalMap,它是一個 Map(key,value)資料格式,key 是一個弱引用,也就是 ThreadLocal 本身,而 value 存的是執行緒變數的值。

也就是說 ThreadLocal 本身並不儲存執行緒的變數值,它只是一個工具,用來維護執行緒內部的 Map,幫助存和取變數。

資料結構,如下圖所示:

Java 200+ 面試題補充 ThreadLocal 模組

(圖片來源於網路)

3.ThreadLocal 如何解決 Hash 衝突?

與 HashMap 不同,ThreadLocalMap 結構非常簡單,沒有 next 引用,也就是說 ThreadLocalMap 中解決 Hash 衝突的方式並非連結串列的方式,而是採用線性探測的方式。所謂線性探測,就是根據初始 key 的 hashcode 值確定元素在 table 陣列中的位置,如果發現這個位置上已經被其他的 key 值佔用,則利用固定的演算法尋找一定步長的下個位置,依次判斷,直至找到能夠存放的位置。

原始碼實現如下:

/
 * Increment i modulo len.
 */
private static int nextIndex(int i, int len) {
    return ((i + 1 < len) ? i + 1 : 0);
}

/
 * Decrement i modulo len.
 */
private static int prevIndex(int i, int len) {
    return ((i - 1 >= 0) ? i - 1 : len - 1);
}
複製程式碼

4.ThreadLocal 的記憶體洩露是怎麼回事?

ThreadLocal 在 ThreadLocalMap 中是以一個弱引用身份被 Entry 中的 Key 引用的,因此如果 ThreadLocal 沒有外部強引用來引用它,那麼 ThreadLocal 會在下次 JVM 垃圾收集時被回收。這個時候 Entry 中的 key 已經被回收,但是 value 又是一強引用不會被垃圾收集器回收,這樣 ThreadLocal 的執行緒如果一直持續執行,value 就一直得不到回收,這樣就會發生記憶體洩露。

5.為什麼 ThreadLocalMap 的 key 是弱引用?

我們知道 ThreadLocalMap 中的 key 是弱引用,而 value 是強引用才會導致記憶體洩露的問題,至於為什麼要這樣設計,這樣分為兩種情況來討論:

  • key 使用強引用:這樣會導致一個問題,引用的 ThreadLocal 的物件被回收了,但是 ThreadLocalMap 還持有 ThreadLocal 的強引用,如果沒有手動刪除,ThreadLocal 不會被回收,則會導致記憶體洩漏。
  • key 使用弱引用:這樣的話,引用的 ThreadLocal 的物件被回收了,由於 ThreadLocalMap 持有 ThreadLocal 的弱引用,即使沒有手動刪除,ThreadLocal 也會被回收。value 在下一次 ThreadLocalMap 呼叫 set、get、remove 的時候會被清除。

比較以上兩種情況,我們可以發現:由於 ThreadLocalMap 的生命週期跟 Thread 一樣長,如果都沒有手動刪除對應 key,都會導致記憶體洩漏,但是使用弱引用可以多一層保障,弱引用 ThreadLocal 不會記憶體洩漏,對應的 value 在下一次 ThreadLocalMap 呼叫 set、get、remove 的時候被清除,算是最優的解決方案。

6.ThreadLocal 的應用場景有哪些?

ThreadLocal 適用於獨立變數副本的情況,比如 Hibernate 的 session 獲取場景。

示例程式碼:

private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();

public static Session getCurrentSession(){
    Session session =  threadLocal.get();
    try {
        if(session ==null&&!session.isOpen()){
            //...
        }
        threadLocal.set(session);
    } catch (Exception e) {
        // TODO: handle exception
    }
    return session;
}
複製程式碼

檢視所有面試題:《Java 最常見 200+ 面試題》

參考資料

www.jianshu.com/p/a1cd61fa2…

www.jianshu.com/p/98b68c97d…

掃描下方二維碼,關注更多動態:

公眾號二維碼

相關文章推薦:

Java 最常見的 200+ 面試題

相關文章