ThreadLocal-從源碼看設計
一、ThreadLocal定義
ThreadLocal提供線程局部變量。這些變量與它們在每個線程中訪問,每個線程有自己的、獨立初始化的變量副本,實例通常是私有的類中希望將狀態與線程關聯的靜態字段。即在每個顯示訪問當前實例對象,都會獲取各自線程對應的值,不會相互影響,不存在線程安全問題,也不會影響程序執行的性能。由於在各自線程存儲對應的值,所以內存消耗會比直接使用實際類對象要大。
二、從源碼看設計
1.get方法
/**
*返回當前線程的副本中的值線程局部變量。
*如果該變量對於當前線程,它首先被初始化爲返回的值
*通過調用{@link #initialValue}方法。
*
* @return 此本地線程的當前線程值
**/
public T get() {
//獲取當前線程
Thread t = Thread.currentThread();
//獲取當前線程的ThreadLocalMap成員變量
ThreadLocalMap map = getMap(t);
//如果當前線程ThreadLocal已經初始化
if (map != null) {
//獲取當前ThreadLocal實例對應的Entry實體
ThreadLocalMap.Entry e = map.getEntry(this);
//如果不爲null,取出Entry對應值
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//如果當前線程ThreadLocal未初始化
return setInitialValue();
}
/**
* 獲取與ThreadLocal關聯的Map。 在InheritableThreadLocal中重寫。
*
* @param t 當前線程
* @return 當前線程的ThreadLocalMap
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/**
* set()的變體來建立initialValue。如果用戶已覆蓋set()方法,請使用它代替set()。
*
* @return 初始化的值
*/
private T setInitialValue() {
//默認返回null
T value = initialValue();
//獲取當前線程
Thread t = Thread.currentThread();
//獲取線程對應map
ThreadLocalMap map = getMap(t);
//map已經實例化,則設置value返回;否則創建ThreadLocalMap
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
/**
* 創建ThreadLocal關聯的map對象,並賦值給當前線程。在InheritableThreadLocal中被重寫
*
* @param t 當前線程
* @param firstValue 初始化map的實體值
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
2. set方法
/**
* 將此線程局部變量的當前線程副本設置爲指定值。 大多數子類將不需要重寫此方法,而僅依靠{@link #initialValue}方法來設置線程局部變量的值。
*
* @param value 要存儲在此本地線程的當前線程副本中的值。
*/
public void set(T value) {
//獲取當前線程
Thread t = Thread.currentThread();
//獲取當前線程對應的ThreadLocalMap
ThreadLocalMap map = getMap(t);
//map不爲空,以當前對象爲key,值爲value設置進map;否則創建map
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
3.總結
get()方法
獲取的值是當前線程對象裏的ThreadLocalMap變量,然後從ThreadLocalMap變量中獲取到對應ThreadLocal對象的值。
set()方法
將要設置的值,保存到從當前線程獲取到的ThreadLocalMap中。
通過這兩個方法,就達到了多線程多個變量副本的效果。
4.ThreadLocalMap
是ThreadLocal的內部類,用於存儲不同ThreadLocal對象和對應的值。內部採用一個初始容量爲16的數組存儲ThreadLocal對應的Entry實體.
static class ThreadLocalMap {
/**
* 此哈希映射中的條目使用其主引用字段作爲鍵(始終是ThreadLocal對象)擴展WeakReference。
請注意,空鍵(即entry.get()== null)意味着不再引用該鍵,因此可以從表中刪除該條目。
在下面的代碼中,此類條目稱爲“陳舊條目”。
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** 這個ThreadLocal關聯的值 */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* 初始容量-必須是2的冪
*/
private static final int INITIAL_CAPACITY = 16;
/**
* 數組表,根據需要調整大小。 table.length必須始終爲2的冪。
*/
private Entry[] table;
/**
* 表格中實體數量
*/
private int size = 0;
/**
* 下一個要調整大小的大小值。
*/
private int threshold; // 默認爲0
}
擴容條件:當數組大小,超過容量的2/3擴容
/**
* 設置調整大小閾值以保持最壞的2/3負載係數。
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
ThreadLocalMap在這裏只簡介,核心重點是分析同步機制,需要深入瞭解具體數據結構的,請查看源碼分析
ThreadLocal和同步機制區別
ThreadLocal:
1.空間換時間
2.在不同線程空間存儲多份實例
同步機制:
1.時間換空間
2.一個實例,不同線程排隊訪問