Java ThreadLocal使用淺析
JAVA API 文檔裡關於 ThreadLocal 的定義是:
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
這個類提供了一個執行緒區域的變數。這些變數在被共用訪問的情況下在不同的線程裡是獨立的 ( 必須通過 get 和 set 方法來訪問 ) 。
很顯然該類提供了一個機制可以防止多線程訪問帶來的不安全機制。實際上就是線上程本地儲存一個變數,而不是通過共用變數。這個就要看我們的使用場合了,如果我們確實需要共用的資料,那還是必須通過同步機制來保證資料的安全。如果有些情況希望不同的線程儲存的變數各自分開,那用這個還是比較合適的。
ThreadLocal 這個類本身不是代表線程要訪問的變數,這個類的成員變數才是。 JDK1.5 給 ThreadLocal 加了泛型功能,即是 ThreadLocal<T>, 這個泛型 T 即是要線程的本地變數。線程通過 ThreadLocal 的 get 和 set 方法去訪問這個變數 T 。 ThreadLocal 提供了一個機制,它能保證線程通過這個來訪問它來訪問類型為 T 的變數的時候是不同的拷貝。所以訪問該變數必須通過 Threadlocal 這個類只提供了兩個 public 方法,即是 get() 和 set ()方法來訪問。
同時還提供了一個 inintValue() 的 protected 方法。該方法用來初始設定變數值。
注意 :預設情況下 initValue(), 返回 null 。線程在沒有調用 set 之前,第一次調用 get 的時候, get 方法會預設去調用 initValue 這個方法。所以如果沒有覆寫這個方法,可能導致 get 返回的是 null 。當然如果調用過 set 就不會有這種情況了。但是往往在多線程情況下我們不能保證每個線程的在調用 get 之前都調用了 set ,所以最好對 initValue 進行覆寫,以免導致null 指標異常。
說了這麼多,估計還不如看下例子吧,下面還是上例子。
例子 1 :沒有使用 Threadlocal 的情況:
/**
*
* @author yblin
* 2009.8.18
*
*/
public class TestWithNoThreadLocal {
public static int a = 0;
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
for ( int i = 0; i < 5; i++) {
a = a + 1;
System. out .println(Thread. currentThread ().getName() + ":" + a );
}
}
public static class MyThread extends Thread {
public void run() {
for ( int i = 0; i < 5; i++) {
a = a + 1;
System. out .println(Thread. currentThread ().getName() + ":" + a );
}
}
}
}
啟動並執行一種結果如下:
main:2
Thread-0:2
main:3
Thread-0:4
main:5
Thread-0:6
main:7
Thread-0:8
main:9
Thread-0:10
在沒有同步的機制下,該結果有不安全的情況發生是正常的,兩個線程都得到 2 。下面請看用了 ThreadLocal 的效果。
例子 2 :使用了 Threadlocal 的情況:
/**
*
* @author yblin
* 2009.8.18
*
*/
public class TestThreadLocal2 {
private static ThreadLocal<Integer> a = new ThreadLocal<Integer>() {
public Integer initialValue() { // 初始化,預設是返回 null
return 0;
}
};
public static void main(String args[]) {
MyThread my;
my = new MyThread();
my.start();
for ( int i = 0; i