從ThreadLocal的作用進行分析,你會更加了解它
本篇文章主要講解Java併發中的一個重要知識點—ThreadLocal類
ThreadLocal簡介
ThreadLocal是一個本地執行緒副本變數類,它也是執行緒內部類的儲存類,可以在指定執行緒內部儲存資料,且只有指定的執行緒可以獲取儲存的資料。
ThreadLocal提供了執行緒記憶體儲變數的能力,這些變數在每一個執行緒中都是相互獨立的,通過set和get方法可以得到當前執行緒對應的值。
ThreadLoacl的相關方法
1.set()方法
public void set(T value) {
//獲取當前執行緒
Thread t = Thread.currentThread();
//儲存的資料結構型別
ThreadLocalMap map = getMap(t);
//如果存在map就直接set,沒有則建立map並set
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
從set()方法可以看到:
1).每次set時都先獲取當前的執行緒,在當前執行緒的基礎上進行操作。
2).獲取該執行緒物件的ThreadLocalMap
3).如果ThreadLocalMap存在,就直接set,沒有就先建立再set
看下ThreadLocalMap
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;
}
}
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
}
看完ThreadLocalMap後發現:ThreadLocalMap是ThreadLocal的一個靜態內部類,其中又建立了靜態類Entry用來儲存變數,且Entry中的key是ThreadLocal的物件,value在ThreadLocal確定的下標位置。
再看一下getMap()方法和createMap()方法
//ThreadLocal類中獲取Thread類中的ThreadLocalMap物件
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//建立map
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
從getMap()可以直接獲取當前執行緒的區域性變數threadLocals(ThreadLocalMap型別)。
從createMap()方法可知:建立map時,例項化一個新的ThreadLocalMap,並賦值給當前執行緒的成員變數threadLocals。
看下threadLocals(ThreadLocalMap型別)區域性變數的建立
ThreadLocal.ThreadLocalMap threadLocals = null;
threadLocals是ThreadLocal中的靜態內部類ThreadLocalMap型別的一個物件,初始化為null。
再看下ThreadLocalMap的set方法就大功告成啦!
//ThreadLocalMap的set方法
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
//獲取 hash 值,用於陣列中的下標
int i = key.threadLocalHashCode & (len-1);
//如果陣列該位置有物件則進入
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//k 相等則覆蓋舊值
if (k == key) {
e.value = value;
return;
}
//此時說明此處 Entry 的 k 中的物件例項已經被回收了,需要替換掉這個位置的 key 和 value
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//建立 Entry 物件
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
//獲取 Entry
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);
}
}
其實,通過原始碼可知,ThreadLocalMap的一些方法和HashMap的方法很類似,都是先獲取key的hash值,確定key在table中的索引位置,然後再判斷set需要修改的位置和當前獲取的位置是否一致,如果一致說明找到陣列位置了則覆蓋對應的值。
總結一下主要步驟如下:
1).建立陣列
2).獲取key的hash值,作為陣列的下標
3).如果陣列當前位置有物件,獲取此位置
4).判斷key和當前陣列位置是不是同一個位置,如果是同一個位置,則說明找到此位置的值,進行覆蓋。
看到這裡大家可能很混亂,為了研究ThreadLocal的set()方法,竟然帶出了一系列的方法,那麼,如何將它們整合到一起呢?
我們還是回到最初的ThreadLocal的set()方法,一步步解讀:
public void set(T value) {
//獲取當前執行緒
Thread t = Thread.currentThread();
//儲存的資料結構型別
ThreadLocalMap map = getMap(t);
//如果存在map就直接set,沒有則建立map並set
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
解讀步驟:
1.呼叫ThreadLocal的set()方法時,會先獲取當前執行緒。
2.獲取ThreadLocal的靜態內部類,通過getMap()方法獲取,getMap()方法返回的是當前執行緒的區域性變數threadLocals(ThreadLocalMap型別)。
3.如果存在ThreadLocalMap的物件,就直接set
4.如果沒有建立ThreadLocalMap的物件,則使用當前執行緒建立,使用creatMap()方法,例項化一個新的ThreadLocalMap,並賦值給當前執行緒的成員變數threadLocals。
總結:
1.ThreadLocal在每個執行緒中都建立了一個ThreadLocalMap物件,且所有的操作都在這個物件裡。
2.ThreadLocalMap是ThreadLocal的靜態內部類,用Entry來進行儲存。
3.呼叫ThreadLocal的set()方法時就是對ThreadLocalMap進行設定值,key是ThreadLocal物件。
ThreadLocal、ThreadLocalMap和Thread的關係
某個執行緒執行->執行緒為每個ThreadLocal建立了一個ThreadLocalMap物件->ThreadLocalMap中用Entry來進行儲存,key為ThreadLocal的引用,value是對應的值。
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();
}
看完set()方法,發現get()方法簡單了許多,其中大體和set()方法類似,從Entry中獲取變數是使用map.getEntry()方法。
總結:我們使用ThreadLocal類是為了使得每個執行緒在處理各自的變數時互不干擾,而ThreadLocal的互不干擾就體現在:每次呼叫其方法時都先獲取當前的執行緒,返回當前執行緒的ThreadLocalMap,實際上ThreadLocal並不會儲存值,真正儲存值得是它的靜態內部類ThreadLocalMap,而ThreadLocal呼叫set()和get()方法時,也會傳遞給ThreadLocalMap進行對應的操作,真正實現了一個執行緒操作執行緒自己的區域性變數。
相關文章
- Java多執行緒10:ThreadLocal的作用及使用Java執行緒thread
- 從自定義一個作用域開始來了解SpringBean的作用域SpringBean
- ThreadLocal的設計理念與作用thread
- java中ThreadLocal作用與執行緒安全問題Javathread執行緒
- ThreadLocal分析thread
- 從0到1進行Spark history分析Spark
- [譯] Flutter — 五個你會愛上它的原因Flutter
- ThreadLocal原理分析thread
- ThreadLocal 原理分析thread
- 你從來沒了解過的CSS浮動 | Design ShackCSS
- ThreadLocal的使用場景分析thread
- 當心 Collection 的 slice 方法,它會偷偷修改你的陣列陣列
- ThreadLocal原始碼分析thread原始碼
- ThreadLocal 原始碼分析thread原始碼
- ThreadLocal必知必會thread
- 用慣了Task,你應該也需要了解它的內部排程機制TaskScheduler
- 寫給那些會做不會說的測試員!“它”正在摧毀你的面試……面試
- 分析從管理員角度對Hadoop進行調優Hadoop
- 對linux從認識到了解---我現在還是用不上它(轉)Linux
- 從try-with-resources到ThreadLocal,最佳化你的程式碼編寫方式!thread
- 學會這些CSS技巧讓你寫樣式更加絲滑CSS
- 從行為軌跡進行大資料分析有什麼好處?大資料
- 前端-JavaScript作用域和執行分析前端JavaScript
- 使用React Hooks你可能會忽視的作用域問題ReactHook
- 來講講你對ThreadLocal的理解thread
- Java併發——ThreadLocal分析Javathread
- ThreadLocal部分原始碼分析thread原始碼
- 火爆網路的《利用 Python 進行資料分析》,有人將它翻譯了中文版!Python
- 3天學會網頁爬蟲進行資料分析網頁爬蟲
- 從 Python 2 切換到 Python 3 你所需要了解的Python
- 從JS的執行機制的角度談談作用域JS
- 執行緒池你真不來了解一下嗎?執行緒
- 成為 AGC 皇帝,從你他她它祂牠鉈做起GC
- 你需要了解的 HTTP Status CodeHTTP
- 你需要了解的HTTP協議HTTP協議
- java中ThreadLocal的應用場景分析Javathread
- 【菜鳥學Java】14:使用ThreadLocal對Connection進行封裝Javathread封裝
- 你的 JS 程式碼本可以更加優雅JS