5 ThreadLocal詳解
關鍵字:ThreadLocal、InheritableThreadLocal、ThreadLocal和局部變量
5.1 ThreadLocal
ThreadLocal是一個泛型類,java.lang.ThreadLocal<T>。
這個類提供線程局部變量。可以將ThreadLocal定義為共享變量(全局變量或者static靜態變量),每個線程在訪問ThreadLocal變量(調用set/get)都會初始化屬於線程的變量副本,可以用於存儲一些與線程相關的狀態、用戶id、事務id等數據。
可以通過子類重寫initialValue()來設置初始值--默認初始值是null。
5.1.1 ThreadLocal應用
為線程設置遞增的線程id,存在於ThreadLocal變量threadids中。
/* 測試ThreadLocal */ public void test1(){ // 原子整數,線程安全 AtomicInteger nextInt = new AtomicInteger(); // 設定threadids的初始值遞增 ThreadLocal<Integer> threadids = new ThreadLocal<Integer>() { @Override protected Integer initialValue() { return nextInt.incrementAndGet();//返回遞增整數 } }; /* 創建5個子線程並輸出threadid的值 */ Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+": "+threadids.get());//在這個時候調用初始化並返回threadid } }; int threadNum = 5; Thread[] threads = new Thread[threadNum]; for(int i=0;i<threadNum;i++){ threads[i] = new Thread(runnable); } /*倒序啟動子線程*/ for(int i=threadNum-1;i>=0;i--){ threads[i].start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println(Thread.currentThread().getName()+": "+threadids.get());//在這個時候調用初始化並返回threadid }
根據代碼結果可以看出來threadids中對某個線程初始化其對應值的時機是該線程調用threadids.get或者threadids.set的時候,所以有如下結果:
Thread-4: 1 Thread-3: 2 Thread-2: 3 Thread-1: 4 Thread-0: 5 main: 6
5.1.2 ThreadLocal.withInitial
Java8中ThreadLocal對象提供了一個Lambda構造方式,實現了非常簡潔的構造方法:withInitial。
這個方法采用Lambda方式傳入實現了 Supplier 函數接口的參數。
// 初始化1 ThreadLocal<Integer> threadids1 = new ThreadLocal<>(); System.out.println("使用new初始化的值:"+threadids1.get()); //初始化2 ThreadLocal<Integer> threadids2 = ThreadLocal.withInitial(()->100); System.out.println("使用withInitial初始化的值:"+threadids2.get()); //初始化3 ThreadLocal<HashMap<Integer,Integer>> threadids3 = ThreadLocal.withInitial(HashMap::new); System.out.println("使用withInitial初始化Map的值:"+threadids3.get());
使用new初始化的值:null 使用withInitial初始化的值:100 使用withInitial初始化Map的值:{}
5.2 InheritableThreadLocal
ThreadLocal在父線程、子線程之間是完全獨立不繼承的。
而InheritableThreadLocal給了子線程初始化繼承父線程ThreadLocal變量值,以及父線程操作子線程初始化值的方法。但僅僅是初始化值的繼承而已,InheritableThreadLocal對於線程依舊是線程獨享的,子線程更改值並不影響父線程。
子線程InheritableThreadLocal變量初始化的值默認繼承子線程運行時父線程的值,但是也可以通過重寫childValue方法來指定子線程初始值和父線程的值的關系。
/* 測試InheritableThreadLocal */ public void test3(){ /* ThreadLocal */ ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); /* 默認繼承父線程值的InheritableThreadLocal */ InheritableThreadLocal<Integer> integerInheritableThreadLocal = new InheritableThreadLocal<>(); /* 設置指定子線程值的InheritableThreadLocal */ InheritableThreadLocal<Integer> integerInheritableThreadLocal2 = new InheritableThreadLocal<Integer>(){ @Override protected Integer childValue(Integer value){ return value+10; } }; /* 主線程設置兩個值都為1 */ threadLocal.set(1); integerInheritableThreadLocal.set(1); integerInheritableThreadLocal2.set(1); /* 子線程中獲取並更改值 */ Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()+":threadLocal= "+threadLocal.get()); System.out.println(Thread.currentThread().getName()+":integerInheritableThreadLocal="+integerInheritableThreadLocal.get()); System.out.println(Thread.currentThread().getName()+":integerInheritableThreadLocal2="+integerInheritableThreadLocal2.get()); integerInheritableThreadLocal.set(2); integerInheritableThreadLocal2.set(2); System.out.println(Thread.currentThread().getName()+":設置 integerInheritableThreadLocal="+integerInheritableThreadLocal.get()); System.out.println(Thread.currentThread().getName()+":設置 integerInheritableThreadLocal2="+integerInheritableThreadLocal2.get()); } }; /* 創建子線程1 */ Thread t1 = new Thread(runnable); t1.start(); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } /* 子線程的數據更改不會影響主線程 */ System.out.println(Thread.currentThread().getName()+":threadLocal= "+threadLocal.get()); System.out.println(Thread.currentThread().getName()+":integerInheritableThreadLocal="+integerInheritableThreadLocal.get()); System.out.println(Thread.currentThread().getName()+":integerInheritableThreadLocal2="+integerInheritableThreadLocal.get()); }
Thread-0:threadLocal= null Thread-0:integerInheritableThreadLocal=1 Thread-0:integerInheritableThreadLocal2=11 Thread-0:設置 integerInheritableThreadLocal=2 Thread-0:設置 integerInheritableThreadLocal2=2 main:threadLocal= 1 main:integerInheritableThreadLocal=1 main:integerInheritableThreadLocal2=1
5.3 ThreadLocal和局部變量
變量線程獨立,其實很容易想到局部變量。在一開始了解到ThreadLocal的定義時,我很難理解,因為我怎么想,這個定義都跟局部變量很像,我無法理解ThreadLocal設計出來的意義。
按照某個說法,可以將局部變量看做是把錢放在自己家里,ThreadLocal變量則是把錢放在銀行,雖然每個人各自的賬號及錢也只能自己訪問,但是錢放在一個同一的地方方便管理。這個區別不是共享變量和局部變量這樣子訪問權限上的區別,更大的區別在於一種設計上、代碼上更加簡單明了。
5.X 參考
0、JAVA多線程編程
Java多線程編程所涉及的知識點包含線程創建、線程同步、線程間通信、線程死鎖、線程控制(掛起、停止和恢復)。之前
-
篇0
-
篇1
-
篇2
-
篇3
-
篇4
-
篇5
-
篇6
-
篇7
-
篇8