ThreadLocal小记
API:
public class ThreadLocal<T> extends Object該類提供了線程局部 (thread-local) 變量。
這些變量不同于它們的普通相應物。由于訪問某個變量(通過其 get 或 set 方法)的每一個線程都有自己的局部變量,它獨立于變量的初始化副本。ThreadLocal 實例一般是類中的 private static 字段,它們希望將狀態與某一個線程(比如,用戶 ID 或事務 ID)相關聯。
每一個線程都保持對其線程局部變量副本的隱式引用,僅僅要線程是活動的而且 ThreadLocal 實例是可訪問的。在線程消失之后。其線程局部實例的全部副本都會被垃圾回收(除非存在對這些副本的其它引用)。
Method:
initialValue
protected T initialValue()線程第一次使用 get() 方法訪問變量時將調用此方法,但假設線程之前調用了 set(T) 方法。則不會對該線程再調用 initialValue 方法。
通常,此方法對每一個線程最多調用一次。但假設在調用get() 后又調用了 remove(),則可能再次調用此方法。
該實現返回 null;假設程序猿希望線程局部變量具有 null 以外的值。則必須為 ThreadLocal 創建子類。并重寫此方法。通常將使用匿名內部類完畢此操作。
get
public T get()set
public void set(T?value)remove
public void remove() 移除此線程局部變量當前線程的值。假設此線程局部變量隨后被當前線程 讀取,且這期間當前線程沒有 設置其值。則將調用其 initialValue() 方法又一次初始化其值。
這將導致在當前線程多次調用 initialValue 方法。
例1:
package com.example;import java.util.Date; import java.util.concurrent.TimeUnit;public class SafeTask implements Runnable{private static ThreadLocal<Date> startDate= new ThreadLocal<Date>() {protected Date initialValue(){return new Date();}};@Overridepublic void run() {System.out.printf("Starting Thread: %s : %s\n",Thread.currentThread().getId(),startDate.get());try {TimeUnit.SECONDS.sleep( (int)Math.rint(Math.random()*10));} catch (InterruptedException e) {e.printStackTrace();}System.out.printf("Thread Finished: %s : %s\n",Thread.currentThread().getId(),startDate.get());}public static void main(String[] args) {SafeTask task=new SafeTask();for (int i=0; i<3; i++){Thread thread=new Thread(task);thread.start();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}}}}執行結果: Starting Thread: 15 : Mon Nov 17 13:36:33 CST 2014 Starting Thread: 17 : Mon Nov 17 13:36:35 CST 2014 Thread Finished: 17 : Mon Nov 17 13:36:35 CST 2014 Starting Thread: 18 : Mon Nov 17 13:36:37 CST 2014 Thread Finished: 15 : Mon Nov 17 13:36:33 CST 2014 Thread Finished: 18 : Mon Nov 17 13:36:37 CST 2014
從結果,能夠看到,三個線程各自存儲和訪問各自維護的startDate局部變量。
解析:
ThreadLocal有一個內部靜態類ThreadLocalMap來管理線程中的變量。能夠簡單的將其理解成一個Map。
而Map中存放的指的方式是:用當前的線程來做為KEY。線程相應的變量值作為VALUE。
當某一線程要獲取當前變量的值時,就使用ThreadLocal.get()方法。通過線程自身作為KEY,去ThreadLocalMap中查找相應的值。
這樣就能夠解釋“每一個線程都保持對其線程局部變量副本的隱式引用”。
而為了使每一個線程都能夠使用該變量的副本使用,“ThreadLocal 實例一般是類中的 private static 字段”。
為了更好的理解ThreadLocal這樣的機制,請看以下的樣例。
例2:
package com.example;import java.util.Date; import java.util.concurrent.TimeUnit;public class SafeTask implements Runnable{private static ThreadLocal<Date> startDate = new ThreadLocal<Date>();@Overridepublic void run() {startDate = new ThreadLocal<Date>();startDate.set(new Date());System.out.printf("Thread: %s new startDate.\n", Thread.currentThread().getId());System.out.printf("Starting Thread: %s : %s\n",Thread.currentThread().getId(),startDate.get());try {TimeUnit.SECONDS.sleep( (int)Math.rint(Math.random()*10));} catch (InterruptedException e) {e.printStackTrace();}System.out.printf("Thread Finished: %s : %s\n",Thread.currentThread().getId(),startDate.get());}public static void main(String[] args) {SafeTask task=new SafeTask();for (int i=0; i<3; i++){Thread thread=new Thread(task);thread.start();try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}}}}運行結果: Thread: 15 new startDate. Starting Thread: 15 : Mon Nov 17 13:55:16 CST 2014 Thread: 17 new startDate. Starting Thread: 17 : Mon Nov 17 13:55:18 CST 2014 Thread Finished: 15 : null Thread Finished: 17 : Mon Nov 17 13:55:18 CST 2014 Thread: 18 new startDate. Starting Thread: 18 : Mon Nov 17 13:55:20 CST 2014 Thread Finished: 18 : Mon Nov 17 13:55:20 CST 2014
為什么例2的結果中會出現null呢?
原因就在于,代碼在線程中又一次創建來ThreadLocal
startDate = new ThreadLocal<Date>();這樣做,就導致了startDate指向了新的ThreadLocal對象。那么之前存放在當中的副本就丟失了,所以才會出現null的情況。通過以上的樣例,我們就能理解為什么ThreadLocal 實例一般是類中的 private static 字段。
我們須要明確。每一個線程中變量的副本,是通過線程的KEY和變量的VALUE存放在ThreadLocalMap中,而不是說。為每一個線程建立ThreadLocal。
就類而言,他實際僅僅維護和管理著一個ThreadLocal。
PS:代碼部分截取來自《Java 7 Concurrency Cookbook》
轉載于:https://www.cnblogs.com/blfshiye/p/5278715.html
總結
以上是生活随笔為你收集整理的ThreadLocal小记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 加减法的生成
- 下一篇: BZOJ1457 棋盘游戏