ThreadLocal原始碼解讀
ThreadLocal的三個理論基礎
1. 每個執行緒都有一個自己的ThreadLocal.ThreadLocalMap物件,ThreadLocal類中定義了靜態類ThreadLocalMap,
靜態類ThreadLocalMap中定義了Entry結構儲存
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
2. 每個ThreadLocal物件都有一個迴圈計數器
3. ThreadLocal.get()取值,就是根據當前的執行緒,獲取執行緒中自己的ThreadLocal.ThreadLocalMap,然後在這個Map中根據根據第二點中迴圈計數器取得一個特定value值
兩個數學問題
1. ThreadLocal.ThreadLocalMap規定了table的大小必須是2的N次冪
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
計算機處理位運算的效率比數學運算要高,例如ThreadLocalMap中獲取Entry物件的方法
private Entry getEntry(ThreadLocal key) {
int i = key.threadLocalHashCode & (table.length - 1);//1
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
上面第一行程式碼,通過與運算獲取到Entry在陣列中的索引
如果使用%的話,table.length=16,存在一個數字23,23%16=7,如果轉為上面的二進位制運算的話:
23 -> 00010111
&
table-1 = 15 -> 00001111
result: 00000111 就是十進位制的 7 ,效率更高
2. 對於上面取模獲取在table中索引位置時候,threadLocalhashCode原始碼如下:
private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
private static final int HASH_INCREMENT = 0x61c88647;
length 為 16 和 32 時候,取模生成的值
通過取模方式獲取索引的時候,每次都會在原來的threadLocalHashCode的基礎上加上0x61c88647,這樣的結果是生成的hash值分散,而且在length擴容為,2的n次冪,之後,生成的hash值會和前面擴容前的值一致,這就保證了threadLocalHashCode可以從任何地方開始。
set(T value)
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
1. 獲取當前執行緒
2. 獲取當前執行緒的ThreadLocalMap,不為null,就往裡面設值
3. 否則,就去建立ThreadLocalMap,並設值
看下,第三步的原始碼:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
建立一個Entry陣列,將初始值放入,取模運算後的索引位置,並且置entry數量為1,而且從ThreadLocalMap中看出並沒有next節點,也就是ThreadLocalMap不是類似於hashmap的連結串列結構,而是開地址法,每次遞增一個值,取模運算計算索引存放元素。這樣的結果就是設定同一個value放到table中的位置會不一樣的
接著,看下,ThreadLocalMap的設值原始碼:
private void set(ThreadLocal key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
1. 取模運算獲取到table陣列的索引位置,從這個索引位置開始遍歷到陣列最後
2. 根據獲取每個Entry的ThreadLocal引用,對於Entry它是個弱引用
static class Entry extends WeakReference<ThreadLocal> {
,獲取到ThreadLocal
3. 如果key和k是指向同一個ThreadLocal,那麼就將值設定到這個Entry上,返回
4. 不是同一個ThreadLocal,在判斷下位置上的ThreadLocal是不是空的,因為Entry是弱引用,有可能這個ThreadLocal已經被垃圾回收了,如果ThreadLocal是空的,會去輪詢找到下一個不為null的entry,將值放在這個entry,並且會去將過期的entry刪除
5. 如果上面都沒有返回的話,將entry數量加1 ,在索引位置設定一個新的Entry
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();
}
private Entry getEntry(ThreadLocal key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
1. 獲取到當前執行緒繫結的ThreadLocalMap
2. 如果ThreadLocalMap不為空
* 如果根據index能直接找到Entry,並且不為null,直接返回這個Entry
* 否則,繼續向下遍歷,找到下一個不為null的entry,返回這個entry,並在輪詢過程中將過期的entry刪除
3. 如果ThreadLocalMap為空
給當前執行緒建立一個ThreadLocalMap,並設定初值
remove()
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
private void remove(ThreadLocal key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
1 . 找到執行緒繫結的ThreadLocalMap,並且找到對應的entry,刪除掉這個entry
總結
1. ThreadLocal不需要鍵值,因為ThreadLocalMap不是通過連結串列實現,而是通過開地址法實現的
2. 設定值的時候,如果index位置找到entry,並且key(這個key指的是ThreadLocal引用)是同一個,進行覆蓋,否則向下找到一個不為null的entry,並設值
3. 查詢資料的時候,如果能夠從index找到entry,直接就返回了,否則就向下繼續找到下一個不為null的entry,返回
4. 如果需要向ThreadLocal中存放不同型別的資料,需要定義多個ThreadLocal
參考部落格:
http://www.cnblogs.com/xrq730/p/4854813.html
相關文章
- ThreadLocal 原始碼解讀thread原始碼
- ThreadLocal原始碼閱讀thread原始碼
- ThreadLocal原始碼解讀和記憶體洩露分析thread原始碼記憶體洩露
- ThreadLocal原始碼thread原始碼
- ThreadLocal原始碼解析thread原始碼
- ThreadLocal原始碼分析thread原始碼
- ThreadLocal 原始碼分析thread原始碼
- ThreadLocal之深度解讀thread
- ThreadLocal部分原始碼分析thread原始碼
- 【原始碼學習】ThreadLocal原始碼thread
- ThreadLocal原始碼深度剖析thread原始碼
- ThreadLocal 原始碼淺析thread原始碼
- PostgreSQL 原始碼解讀(3)- 如何閱讀原始碼SQL原始碼
- WeakHashMap,原始碼解讀HashMap原始碼
- Handler原始碼解讀原始碼
- Laravel 原始碼解讀Laravel原始碼
- Swoft 原始碼解讀原始碼
- SDWebImage原始碼解讀Web原始碼
- MJExtension原始碼解讀原始碼
- Masonry原始碼解讀原始碼
- HashMap原始碼解讀HashMap原始碼
- Redux原始碼解讀Redux原始碼
- require() 原始碼解讀UI原始碼
- ZooKeeper原始碼解讀原始碼
- FairyGUI原始碼解讀AIGUI原始碼
- 【C++】【原始碼解讀】std::is_same函式原始碼解讀C++原始碼函式
- 執行緒封閉之ThreadLocal原始碼詳解執行緒thread原始碼
- Thread、ThreadLocal原始碼解析thread原始碼
- ThreadLocal底層原始碼解析thread原始碼
- vuex 原始碼:原始碼系列解讀總結Vue原始碼
- Laravel 原始碼的解讀Laravel原始碼
- reselect原始碼解讀原始碼
- Redux原始碼完全解讀Redux原始碼
- Seajs原始碼解讀JS原始碼
- Axios 原始碼解讀iOS原始碼
- HashMap原始碼個人解讀HashMap原始碼
- Vue原始碼解讀一Vue原始碼
- Slim 框架原始碼解讀框架原始碼