1. 程式人生 > >ThreadLocal詳解(不看後悔,一看必懂)

ThreadLocal詳解(不看後悔,一看必懂)

ThreadLocal概要

我們可以使用synchorinized 關鍵字來為變數加鎖以解決執行緒安全問題,從而限制只能有一個執行緒來使用此變數,但是加鎖會大大影響程式執行效率

ThreadLocal是執行緒區域性變數(從執行緒的角度看,目標變數就像是執行緒的本地變數), 通常是類中的 private static 欄位. 當ThreadLocal維護變數的時候,為每一個使用該變數的執行緒都提供一個獨立的變數副本.即每個執行緒內部都會有一個該變數,這樣同時多個執行緒訪問該變數並不會彼此相互影響.這樣就不存線上程安全問題。但是由於在每個執行緒中都建立了副本,所以它對記憶體資源的消耗要大一些,其實它採用了空間換時間的思想

Thread 在內部是通過ThreadLocalMap來維護ThreadLocal變量表, 在Thread類中有一個threadLocals 變數,是ThreadLocalMap型別的,它就是為每一個執行緒來儲存自身的ThreadLocal變數的, ThreadLocalMap是ThreadLocal類的一個內部類,這個Map裡面可以有多個Entry, 每一個Entry使用ThreadLocal作為key, 變數作為 value

(1)ThreadLocal模式至少從兩個方面完成了資料訪問隔離,即縱向隔離(執行緒與執行緒之間的ThreadLocalMap不同)和橫向隔離(不同的ThreadLocal例項之間的互相隔離)

(2)ThreadLocalMap變數屬於執行緒的內部屬性,不同的執行緒擁有完全不同的ThreadLocalMap變數;

(3)執行緒中的ThreadLocalMap變數的值是在ThreadLocal物件進行set或者get操作時建立的;

在上圖中的一個Thread的這個ThreadLocalMap中分別存放了3個Entry。每一個Entry物件存放的是一個ThreadLocal變數物件。

ThreadLocal方法

public T get() { } // 用來獲取該ThreadLocal在當前執行緒中儲存的變數副本

public void set(T value) { } //用來設定當前執行緒中變數的副本

public void remove() { } //remove()用來移除當前執行緒中變數的副本

protected T initialValue() { } //initialValue()是一個protected方法,一般是用來在使用時進行重寫的

ThreadLocal的使用場景

​​​​​​​​​​​​​​ThreadLocal可能引起的OOM記憶體溢位問題

我們知道ThreadLocal變數是維護在Thread內部的,這樣的話只要我們的執行緒不退出,物件的引用就會一直存在。當執行緒退出時,Thread類會進行一些清理工作,其中就包含ThreadLocalMap,Thread在原始碼中是呼叫exit方法進行清理的。但是當我們使用執行緒池的時候,就意味著當前執行緒未必會退出(比如固定大小的執行緒池,執行緒總是存在的)。如果這樣的話,將一些很大的物件設定到ThreadLocal中(這個很大的物件實際儲存在Thread的threadLocals屬性中),這樣的話就可能會出現記憶體溢位的情況。ThreadLocal在沒有執行緒池使用的情況下,正常情況下不會存在記憶體洩露,但是如果使用了執行緒池的話,就依賴於執行緒池的實現,如果執行緒池不銷燬執行緒的話,那麼就會存在記憶體洩露。所以我們在使用執行緒池的時候,使用ThreadLocal要格外小心!​​​​​​​