例如,在下面的類中,私有靜態
ThreadLocal 實例(
serialNum)爲調用該類的靜態
SerialNum.get() 方法的每一個線程維護了一個「序列號」,該方法將返回當前線程的序列號。(線程的序列號是在第一次調用
SerialNum.get() 時分配的,並在後續調用中不會更改。)
每一個線程都保持一個對其線程局部變量副本的隱式引用,只要線程是活動的而且
ThreadLocal 實例是可訪問的;在線程消失以後,其線程局部實例的全部副本都會被垃圾回收(除非存在對這些副本的其餘引用)。
方法摘要 |
T |
get() 返回此線程局部變量的當前線程副本中的值。 |
protected T |
initialValue() 返回此線程局部變量的當前線程的初始值。 |
void |
remove() 移除此線程局部變量的值。 |
void |
set(T value) 將此線程局部變量的當前線程副本中的值設置爲指定值。 |
其中,還給出了一個頗有用的例子片斷,是一個線程序號維護工具,頗有用,我通過完善後以下:
/**
* 線程序號標識生成工具
*
* @author leizhimin 2008-8-21 21:28:54
*/
public
class SerialNum {
//類級別的線程編號變量,指向下一個線程的序號
private
static Integer nextNum = 0;
//定義一個ThreadLocal變量,存放的是Integer類型的線程序號
private
static ThreadLocal<Integer> threadNo =
new ThreadLocal<Integer>() {
//經過匿名內部類的方式定義ThreadLocal的子類,覆蓋initialValue()方法
public
synchronized Integer initialValue() {
return nextNum++;
}
};
/**
* 獲取線程序號
*
* @return 線程序號
*/
public
int getNextNum() {
return threadNo.get().intValue();
}
}
/**
* 一個多線程對象,其中有個私有變量SerialNum,用來保存該對象線程的序號
*
* @author leizhimin 2008-8-21 21:52:47
*/
class MultiThreadObject
extends Thread {
//線程序號變量
private SerialNum serialNum;
public MultiThreadObject(SerialNum serialNum) {
//初始化線程序號保存變量
this.serialNum = serialNum;
}
/**
* 一個示意性的多線程業務方法
*/
public
void run() {
System.out.println(
"線程" + Thread.currentThread().getName() +
"的序號爲" + serialNum.getNextNum());
}
}
/**
* 測試線程序號工具
*
* @author leizhimin 2008-8-21 21:50:44
*/
public
class TestTreadLocal {
public
static
void main(String[] args) {
SerialNum serialNum =
new SerialNum();
MultiThreadObject m1 =
new MultiThreadObject(serialNum);
MultiThreadObject m2 =
new MultiThreadObject(serialNum);
MultiThreadObject m3 =
new MultiThreadObject(serialNum);
MultiThreadObject m4 =
new MultiThreadObject(serialNum);
m1.start();
m2.start();
m3.start();
m4.start();
//下面的test方法是在主線程中,當前線程是
//test();
}
public
static
void test(){
SerialNum serialNum =
new SerialNum();
System.out.println(serialNum.getNextNum());
SerialNum serialNum2 =
new SerialNum();
System.out.println(serialNum2.getNextNum());
}
}
運行結果:
線程Thread-0的序號爲0
線程Thread-1的序號爲1
線程Thread-2的序號爲2
線程Thread-3的序號爲3
Process finished with exit code 0
JDK中這個例子的高明之處在於巧妙使用靜態變量,結合ThreadLocal的特性,在構件ThreadLocal的時候,經過覆蓋子類的方法來改寫序號。從而達到爲每一個線程生成序號的目的。
ThreadLocal理解
ThreadLocal是線程的局部變量,經常使用來爲每一個線程提供獨立的變量副本(可理解拷貝),沒個線程能夠隨意改變其副本,而不會影響原版。
ThreadLocal是Java在經過語言的擴展而來的,並不是從語法級(原生)支持。這是致使ThreadLocal概念很差理解的主要緣由。有兩種途徑能夠幫助理解,一是查看ThreadLocal的源代碼(JAVA已經開源了),其次是經過一個簡單的ThreadLocal實現來看其原理。源碼均可以看,但比較複雜。仍是看個簡單ThreadLocal實現吧:
import java.util.Map;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
/**
* 一個示意性的ThreadLocal實現,與JDK中ThreadLocal的API對等
*
* @author leizhimin 2008-8-21 22:45:13
*/
public
class SimpleThreadLocal {
//一個線程Map,用來存放線程和其對應的變量副本
private Map threadMap = Collections.synchronizedMap(
new HashMap());
public
void set(Object object) {
threadMap.put(Thread.currentThread(), object);
}
public Object get() {
Thread currentThread = Thread.currentThread();
Object obj = threadMap.get(currentThread);
if (obj ==
null && !threadMap.containsKey(currentThread)) {
obj = initialValue();
threadMap.put(currentThread, obj);
}
return obj;
}
public
void remove() {
threadMap.remove(Thread.currentThread());
}
protected Object initialValue() {
return
null;
}
}
上面這個類能夠替代JDK中ThreadLocal來實現一樣的功能。我已經試過了,只須要修改SerialNum的實現爲:
/**
* 線程序號標識生成工具
*
* @author leizhimin 2008-8-21 21:28:54
*/
public
class SerialNum {
//類級別的線程編號變量,指向下一個線程的序號
private
static Integer nextNum = 0;
//定義一個ThreadLocal變量,存放的是Integer類型的線程序號
// private static ThreadLocal<Integer> threadNo = new ThreadLocal<Integer>() {
private
static SimpleThreadLocal threadNo =
new SimpleThreadLocal() {
//經過匿名內部類的方式定義ThreadLocal的子類,覆蓋initialValue()方法
public
synchronized Integer initialValue() {
return nextNum++;
}
};
/**
* 獲取線程序號
*
* @return 線程序號
*/
public
int getNextNum() {
return (Integer)threadNo.get();
}
}
線程Thread-0的序號爲0
線程Thread-1的序號爲1
線程Thread-2的序號爲2
線程Thread-3的序號爲3
Process finished with exit code 0
能夠看出,JDK中TreadLocal實現只是考慮更周密一些罷了,思想是一致的。