Java ThreadLocal 使用及實現原理
一. ThreadLocal是什麼
ThreadLocal
是執行緒本地資料儲存類,通過ThreadLocal
可以在特定的執行緒中儲存資料和變數, 並且這些資料之後只能由該執行緒訪問,其他執行緒是訪問不了的, 保證各個執行緒裡資料和變數的獨立性; 即ThreadLocal
使每個執行緒可以訪問自己內部的副本變數。
二. 通過例子來了解ThreadLocal
下面通過一個簡單的例子來說明ThreadLocal
的作用以及使用;
程式碼如下:
public class ThreadLocalDemo {
private static ThreadLocal<String> mThreadLocalDesc = new ThreadLocal<>();
public static class Thread1 extends Thread {
@Override
public void run() {
super.run();
mThreadLocalDesc.set("this is thread-1");
System.out.println("Thread1 mThreadLocalDesc = "+mThreadLocalDesc.get());
}
}
public static class Thread2 extends Thread {
@Override
public void run() {
super.run();
mThreadLocalDesc.set("this is thread-2");
System.out.println("Thread2 mThreadLocalDesc = "+mThreadLocalDesc.get());
}
}
public static void main(String[] args) {
mThreadLocalDesc.set("this is main thread" );
System.out.println("main mThreadLocalDesc = "+mThreadLocalDesc.get());
Thread1 thread1 = new Thread1();
Thread2 thread2 = new Thread2();
thread1.start();
thread2.start();
}
}
我們分別使用ThreadLocal
來在Thread1, Thread2 和 main執行緒這三個執行緒裡設定相應的變數, 並且都打印出來,檢驗是否儲存了執行緒各自變數的值, 程式執行結果如下:
三.原始碼解析ThreadLocal類
實現思想,每個執行緒維護一個
ThreadLocalMap
的對映表,對映表的key
是ThreadLocal
例項本身,value
是要儲存的副本變數。ThreadLocal
例項本身並不儲存值,它只是提供一個在當前執行緒中找到副本值的 key。
ThreadLocal 類定義如下:
public class ThreadLocal<T>
從類的宣告可以看出ThreadLocal
是一個泛型類, 所以它可以用來儲存任何型別的執行緒本地資料;
ThreadLocal類提供的幾個方法:
public T get() { }
get 方法是用來獲取 ThreadLocal 在當前執行緒中儲存的變數副本
public void set(T value) { }
set 用來設定當前執行緒中變數的副本
public void remove() { }
remove 用來移除當前執行緒中變數的副本
protected T initialValue() { }
initialValue 是一個protected方法,一般是用來在使用時進行重寫的,做初始化操作
1. 先分析set方法的實現:
public void set(T value) {
//取得當前執行緒
Thread t = Thread.currentThread();
//獲取當前執行緒 ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
Thread類的threadLocals欄位定義如下:
//與此執行緒相關的ThreadLocal值。這個對映由ThreadLocal類維護。
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap
來儲存每個執行緒副本變數,它是ThreadLocal
裡的一個靜態內部類。ThreadLocalMap
也是採用的散列表(Hash)思想來實現的
ThreadLocalMap
的實現方式:
Map
是一種 key-value
形式的資料結構,ThreadLocalMap
使用 Entry
類來儲存資料,下面是Entry
類的定義:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry
將 ThreadLocal
例項作為 key
,副本變數作為 value
儲存起來, Entry
中對於 ThreadLocal
例項的引用是一個弱引用;
整個 set
方法就是先取得當前執行緒,然後通過 getMap(t) 方法獲取到一個Map
,Map
的型別為ThreadLocalMap
, 如果此時獲取到的map = null
,就是先建立ThreadLocalMap
, 否則就儲存該變數, 注意這裡獲取鍵值對傳進去的是 this,而不是當前執行緒t.
2. 再分析get方法的實現:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
get
方法也是先獲取當前執行緒, 然後再獲取ThreadLocalMap
, 通過ThreadLocalMap
的getEntry(this)
直接獲取value;
如果在map
中查詢不到對應的儲存,則會通過呼叫setInitialValue方法返回初始化時的初始值,而預設情況下,initialValue方法返回的是null。