標籤:總結 方式 回收 元素 map jdk 好的 儲存 create
前言:工作中將要使用ThreadLocal,先學習總結一波。有不對的地方歡迎評論指出。
定義
ThreadLocal並不是一個Thread,而是Thread的局部變數。這些變數不同於它們的普通對應物,因為訪問某個變數(通過其 get 或 set 方法)的每個線程都有自己的局部變數,它獨立於變數的初始化副本。
作用
實現每一個線程都有自己的共用變數。
使用方法
initialValue:返回該線程局部變數的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的,預設就是null。
remove方法:將當前線程局部變數的值刪除,目的是為了減少記憶體的佔用,該方法是JDK 1.5 新增的方法。需要指出的是,當線程結束後,對應該線程的局部變數將自動被記憶體回收,所以顯式調用該方法清除線程的局部變數並不是必須的操作,但它可以加快記憶體回收的速度。
源碼解析 ThreadLocal 構造方法:
initialValue() 方法:
get() 方法:
第一行為擷取線程的當前活動線程
然後擷取到當前線程的ThreadLocalMap 對象,再通過當前threadLocal來擷取這個map對象的索引值對,從而取出當前threadLocal中存的變數副本。
如果ThreadLocalMap 對象為空白,或這個map裡面還沒有存當前threadLocal的變數副本,則調用setInitialValue();
set() 方法:
如果當前線程裡面有線程變數map,則給當前線程變數(this)設定值(value);如果沒有,則建立當前線程的線程變數map,並設定值。
getMap() 方法:
getMap() 方法中,返回了當前線程的變數,threadLocals,類型為ThreadLocalMap。
setInitialValue() 方法:
setInitialValue() 方法,主要是設定初始化的 當前線程變數的變數副本。如果當前線程裡面還沒有 當前線程變數Map(ThreadLocalMap),則,初始化當前線程(thread)的線程變數Map
createMap() 方法:
初始化當前線程(thread)的線程變數Map
ThreadLocalMap 內部類:
構造方法:
從這裡可以看出,threadLocalMap裡面存的key值就是 ThreadLocal 對象。
remove() 方法:
舉例 驗證線程變數的隔離性
1 /** 2 * 本地線程變數 test 3 * Created by yule on 2018/6/26 22:35. 4 */ 5 public class ThreadLocalTest { 6 public static void main(String[] args) throws InterruptedException { 7 ThreadDemo1 threadDemo1 = new ThreadDemo1(); 8 threadDemo1.start(); 9 10 Thread.sleep(100);11 12 ThreadDemo2 threadDemo2 = new ThreadDemo2();13 threadDemo2.start();14 15 ThreadLocalTools.stringThreadLocal.set("main設定值");16 System.out.println(ThreadLocalTools.stringThreadLocal.get());17 }18 }19 20 class ThreadLocalTools{21 public static ThreadLocal<String> stringThreadLocal = new ThreadLocal<>();22 }23 24 class ThreadDemo1 extends Thread{25 @Override26 public void run() {27 super.run();28 for(int i = 0; i < 10; i++){29 System.out.println(ThreadLocalTools.stringThreadLocal.get());30 ThreadLocalTools.stringThreadLocal.set("ThreadDemo1設定值");31 try {32 Thread.sleep(100);33 } catch (InterruptedException e) {34 e.printStackTrace();35 }36 }37 }38 }39 40 class ThreadDemo2 extends Thread{41 @Override42 public void run() {43 super.run();44 for(int i = 0; i < 10; i++){45 System.out.println(ThreadLocalTools.stringThreadLocal.get());46 ThreadLocalTools.stringThreadLocal.set("ThreadDemo2設定值");47 try {48 Thread.sleep(100);49 } catch (InterruptedException e) {50 e.printStackTrace();51 }52 }53 }54 }
輸出的第一個為null是因為在set()方法前調用get()方法,會給出initialValue()方法的值,預設為null。
總結
ThreadLocal是解決安全執行緒問題一個很好的思路,它通過為每個線程提供一個獨立的變數副本解決了變數並發訪問的衝突問題。在很多情況下,ThreadLocal比直接使用synchronized同步機制解決安全執行緒問題更簡單,更方便,且結果程式擁有更高的並發性。
ThreadLocal通常是類中的 private static 欄位,它們希望將狀態與某一個線程(例如,使用者識別碼 或事務 ID)相關聯。線上程消失之後,其線程局部執行個體的所有副本都會被記憶體回收(除非存在對這些副本的其他引用)。
ThreadLocal是如何做到為每一個線程維護變數的副本的呢?其實實現的思路很簡單:在ThreadLocal類中定義了一個ThreadLocalMap,每一個Thread中都有一個該類型的變數——threadLocals——用於儲存每一個線程的變數副本,Map中元素的鍵為線程對象,而值對應線程的變數副本。
通常我們通過匿名內部類的方式定義ThreadLocal的子類,提供初始的變數值。
Java 類 ThreadLocal 本地線程變數