這篇文章主要介紹了ThreadLocal的實(shí)現(xiàn)原理是什么,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。
創(chuàng)新互聯(lián)建站是網(wǎng)站建設(shè)專家,致力于互聯(lián)網(wǎng)品牌建設(shè)與網(wǎng)絡(luò)營銷,專業(yè)領(lǐng)域包括成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)、電商網(wǎng)站制作開發(fā)、微信平臺小程序開發(fā)、微信營銷、系統(tǒng)平臺開發(fā),與其他網(wǎng)站設(shè)計(jì)及系統(tǒng)開發(fā)公司不同,我們的整合解決方案結(jié)合了恒基網(wǎng)絡(luò)品牌建設(shè)經(jīng)驗(yàn)和互聯(lián)網(wǎng)整合營銷的理念,并將策略和執(zhí)行緊密結(jié)合,且不斷評估并優(yōu)化我們的方案,為客戶提供全方位的互聯(lián)網(wǎng)品牌整合方案!
ThreadLocal,即線程局部變量,用來為每一個(gè)使用它的線程維護(hù)一個(gè)獨(dú)立的變量副本。這種變量只在線程的生命周期內(nèi)有效。并且與鎖機(jī)制那種以時(shí)間換取空間的做法不同,ThreadLocal沒有任何鎖機(jī)制,它以空間換取時(shí)間的方式保證變量的線程安全。
本篇從源碼方面分析ThreadLocal的實(shí)現(xiàn)原理。
先看一下ThreadLocal類圖結(jié)構(gòu)

SuppliedThreadLocal主要是JDK1.8用來擴(kuò)展對Lambda表達(dá)式的支持,有興趣的自行百度。
ThreadLocalMap是ThreadLocal的靜態(tài)內(nèi)部類,也是實(shí)際保存變量的類。
Entry是ThreadLocalMap的靜態(tài)內(nèi)部類。ThreadLocalMap持有一個(gè)Entry數(shù)組,以ThreadLocal為key,變量為value,封裝一個(gè)Entry。
下面以一張圖簡要說明Thread,ThreadLocal,ThreadLocalMap和Entry的關(guān)系。

說明一下上圖:
一個(gè)Thread擁有一個(gè)ThreadLocalMap對象
ThreadLocalMap擁有一個(gè)Entry數(shù)組
每個(gè)Entry都有k--v
Entry的key就是某個(gè)具體的ThreadLocal對象
下面分析主要方法。
1、set()
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}這里可以看出:一個(gè)Thread只擁有一個(gè)ThreadLocalMap對象;具體存值調(diào)用的是ThreadLocalMap的set(),傳入的參數(shù)key就是當(dāng)前ThreadLocal對象。
再看看ThreadLocalMap的set()方法:
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1); // 1
for (Entry e = tab[i]; // 2
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); // 3
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold) // 4
rehash();
}通過key的hashCode與數(shù)組容量 -1 取模,計(jì)算數(shù)組index
從當(dāng)前index開始遍歷,清除key為null的無效Entry
將K-V封裝為Entry,并放入數(shù)組
判斷是否需要進(jìn)行Entry數(shù)組擴(kuò)容。threshold的值為數(shù)組容量的2/3。
看看擴(kuò)容的resize()方法:
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0;
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal<?> k = e.get();
if (k == null) {
e.value = null; // Help the GC
} else {
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
setThreshold(newLen);
size = count;
table = newTab;
}這里主要就是擴(kuò)容為原先的2倍。然后遍歷舊數(shù)組,根據(jù)新數(shù)組容量重新計(jì)算Entry在新數(shù)組中的位置。
2、get()
ThreadLocal的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();
}ThreadLocalMap的getEntry()方法如下:
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1); // 1
Entry e = table[i];
if (e != null && e.get() == key) // 2
return e;
else
return getEntryAfterMiss(key, i, e); //3
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) { //4
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}計(jì)算index
當(dāng)前index上的Entry不為空且key相同,直接返回
否則去相鄰index尋找
循環(huán)查找,發(fā)現(xiàn)無效key就清除。找到就結(jié)束循環(huán)。
3、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;
}
}
}處理方式和查找保存類似,刪除對應(yīng)Entry后都會去除key為null的無效元素。
注意
static class Entry extends WeakReference<ThreadLocal<?>> {}ThreadLocal可能存在OOM問題。因?yàn)門hreadLocalMap是使用ThreadLocal的弱引用作為key的,發(fā)生GC時(shí),key被回收,這樣我們就無法訪問key為null的value元素,如果value本身是較大的對象,那么線程一直不結(jié)束的話,value就一直無法得到回收。特別是在我們使用線程池時(shí),線程是復(fù)用的,不會殺死線程,這樣ThreadLocal弱引用被回收時(shí),value不會被回收。
在使用ThreadLocal時(shí),線程邏輯代碼結(jié)束時(shí),必須顯示調(diào)用ThreadLocal.remove()方法。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享ThreadLocal的實(shí)現(xiàn)原理是什么內(nèi)容對大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問題就找創(chuàng)新互聯(lián),詳細(xì)的解決方法等著你來學(xué)習(xí)!
網(wǎng)站標(biāo)題:ThreadLocal的實(shí)現(xiàn)原理是什么
當(dāng)前路徑:http://www.chinadenli.net/article16/peeedg.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、虛擬主機(jī)、外貿(mào)建站、企業(yè)網(wǎng)站制作、網(wǎng)頁設(shè)計(jì)公司、網(wǎng)站設(shè)計(jì)公司
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)