ThreadLocal小记
API:
public class ThreadLocal<T> extends Object該類(lèi)提供了線程局部 (thread-local) 變量。
這些變量不同于它們的普通相應(yīng)物。由于訪問(wèn)某個(gè)變量(通過(guò)其 get 或 set 方法)的每一個(gè)線程都有自己的局部變量,它獨(dú)立于變量的初始化副本。ThreadLocal 實(shí)例一般是類(lèi)中的 private static 字段,它們希望將狀態(tài)與某一個(gè)線程(比如,用戶 ID 或事務(wù) ID)相關(guān)聯(lián)。
每一個(gè)線程都保持對(duì)其線程局部變量副本的隱式引用,僅僅要線程是活動(dòng)的而且 ThreadLocal 實(shí)例是可訪問(wèn)的。在線程消失之后。其線程局部實(shí)例的全部副本都會(huì)被垃圾回收(除非存在對(duì)這些副本的其它引用)。
Method:
initialValue
protected T initialValue()線程第一次使用 get() 方法訪問(wèn)變量時(shí)將調(diào)用此方法,但假設(shè)線程之前調(diào)用了 set(T) 方法。則不會(huì)對(duì)該線程再調(diào)用 initialValue 方法。
通常,此方法對(duì)每一個(gè)線程最多調(diào)用一次。但假設(shè)在調(diào)用get() 后又調(diào)用了 remove(),則可能再次調(diào)用此方法。
該實(shí)現(xiàn)返回 null;假設(shè)程序猿希望線程局部變量具有 null 以外的值。則必須為 ThreadLocal 創(chuàng)建子類(lèi)。并重寫(xiě)此方法。通常將使用匿名內(nèi)部類(lèi)完畢此操作。
get
public T get()set
public void set(T?value)remove
public void remove() 移除此線程局部變量當(dāng)前線程的值。假設(shè)此線程局部變量隨后被當(dāng)前線程 讀取,且這期間當(dāng)前線程沒(méi)有 設(shè)置其值。則將調(diào)用其 initialValue() 方法又一次初始化其值。
這將導(dǎo)致在當(dāng)前線程多次調(diào)用 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();}}}}執(zhí)行結(jié)果: 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
從結(jié)果,能夠看到,三個(gè)線程各自存儲(chǔ)和訪問(wèn)各自維護(hù)的startDate局部變量。
解析:
ThreadLocal有一個(gè)內(nèi)部靜態(tài)類(lèi)ThreadLocalMap來(lái)管理線程中的變量。能夠簡(jiǎn)單的將其理解成一個(gè)Map。
而Map中存放的指的方式是:用當(dāng)前的線程來(lái)做為KEY。線程相應(yīng)的變量值作為VALUE。
當(dāng)某一線程要獲取當(dāng)前變量的值時(shí),就使用ThreadLocal.get()方法。通過(guò)線程自身作為KEY,去ThreadLocalMap中查找相應(yīng)的值。
這樣就能夠解釋“每一個(gè)線程都保持對(duì)其線程局部變量副本的隱式引用”。
而為了使每一個(gè)線程都能夠使用該變量的副本使用,“ThreadLocal 實(shí)例一般是類(lèi)中的 private static 字段”。
為了更好的理解ThreadLocal這樣的機(jī)制,請(qǐng)看以下的樣例。
例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();}}}}運(yùn)行結(jié)果: 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的結(jié)果中會(huì)出現(xiàn)null呢?
原因就在于,代碼在線程中又一次創(chuàng)建來(lái)ThreadLocal
startDate = new ThreadLocal<Date>();這樣做,就導(dǎo)致了startDate指向了新的ThreadLocal對(duì)象。那么之前存放在當(dāng)中的副本就丟失了,所以才會(huì)出現(xiàn)null的情況。通過(guò)以上的樣例,我們就能理解為什么ThreadLocal 實(shí)例一般是類(lèi)中的 private static 字段。
我們須要明確。每一個(gè)線程中變量的副本,是通過(guò)線程的KEY和變量的VALUE存放在ThreadLocalMap中,而不是說(shuō)。為每一個(gè)線程建立ThreadLocal。
就類(lèi)而言,他實(shí)際僅僅維護(hù)和管理著一個(gè)ThreadLocal。
PS:代碼部分截取來(lái)自《Java 7 Concurrency Cookbook》
轉(zhuǎn)載于:https://www.cnblogs.com/blfshiye/p/5278715.html
總結(jié)
以上是生活随笔為你收集整理的ThreadLocal小记的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 加减法的生成
- 下一篇: BZOJ1457 棋盘游戏