Java——ThreadLocal類
阿新 • • 發佈:2019-02-15
一,引入ThreadLocal
/*測試ThreadLocal物件
* ThreadLocal:這個類提供了一個執行緒本地的變數。
* 這些變數在被共享訪問的情況下在不同的執行緒裡是獨立的 ( 必須通過 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 進行覆寫,以免導致空指標異常。
* */
測試,在沒有ThreadLocal的時候:
public class TestThreadLocal {
public static int a = 0;
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
for(int i=0;i<5;i++){
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;
System.out.println(Thread.currentThread().getName()+":"+a);
}
}
}
}
測試結果:
main:1
main:2
main:3
main:4
main:5
Thread-0:6
Thread-0:7
Thread-0:8
Thread-0:9
Thread-0:10
發現結果很混亂,兩個執行緒的執行明顯有交叉。
使用ThreadLocal之後:
public class TestThreadLocal {
//public static int a = 0;
public static ThreadLocal<Integer> a=new ThreadLocal<Integer>(){
public Integer initialValue(){//初始化a的值
return 0;
}
};
public static void main(String[] args) {
MyThread myThread=new MyThread();
myThread.start();
for(int i=0;i<5;i++){
//a=++a;
a.set(a.get()+1);
System.out.println(Thread.currentThread().getName()+":"+a.get());
}
}
public static class MyThread extends Thread {
public void run(){
for(int i=0;i<5;i++){
//a=++a;
a.set(a.get()+1);
System.out.println(Thread.currentThread().getName()+":"+a.get());
}
}
}
}
結果:
Thread-0:1
Thread-0:2
Thread-0:3
Thread-0:4
Thread-0:5
main:1
main:2
main:3
main:4
main:5
使用了執行緒本地變數之後,我們必須通過get和set方法對變數進行讀寫。
二,簡單介紹ThreadLocal
/**
* Returns the current thread's "initial value" for this
* thread-local variable. This method will be invoked the first
* time a thread accesses the variable with the {@link #get}
* method, unless the thread previously invoked the {@link #set}
* method, in which case the <tt>initialValue</tt> method will not
* be invoked for the thread. Normally, this method is invoked at
* most once per thread, but it may be invoked again in case of
* subsequent invocations of {@link #remove} followed by {@link #get}.
*
* <p>This implementation simply returns <tt>null</tt>; if the
* programmer desires thread-local variables to have an initial
* value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
* subclassed, and this method overridden. Typically, an
* anonymous inner class will be used.
*
* @return the initial value for this thread-local
*/
protected T initialValue() {
return null;
}
變數的初始化操作在這裡完成,預設返回null,建議override此方法,確保get之前已進行過set操作,防止get的時候出錯。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
在ThreadLocal類中,有一個內部靜態 類ThreadLocalMap,在這裡存放著以key為當前threadLocal的object。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
set方法也是這樣類似的,ThreadLocal裡面通常放入的值通常就是採用這兩種方法進行操作的,哦,還有remove:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
好多程式碼のの。。。。大致先瞅瞅,然後接著切換回我們的spring原始碼解析,這篇只是小 插曲。