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()
傳回此線程局部變量的目前線程副本中的值。假設變量沒實用于目前線程的值,則先将其初始化為調用
initialValue()
方法傳回的值。
此線程局部變量的目前線程的值
set
public void set(T value)
将此線程局部變量的目前線程副本中的值設定為指定值。大部分子類不須要重寫此方法,它們僅僅依靠
initialValue()
方法來設定線程局部變量的值。
參數:
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();
}
};
@Override
public 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>();
@Override
public 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。