前言:工作中將要使用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 @Override 26 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 @Override 42 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 或事務 ID)相關聯。在線程消失之后,其線程局部實例的所有副本都會被垃圾回收(除非存在對這些副本的其他引用)。
ThreadLocal是如何做到為每一個線程維護變量的副本的呢?其實實現的思路很簡單:在ThreadLocal類中定義了一個ThreadLocalMap,每一個Thread中都有一個該類型的變量——threadLocals——用於存儲每一個線程的變量副本,Map中元素的鍵為線程對象,而值對應線程的變量副本。
通常我們通過匿名內部類的方式定義ThreadLocal的子類,提供初始的變量值。