ThreadLocal的實(shí)現(xiàn)原理是什么-創(chuàng)新互聯(lián)

這篇文章主要介紹了ThreadLocal的實(shí)現(xiàn)原理是什么,具有一定借鑒價(jià)值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。

創(chuàng)新互聯(lián)長(zhǎng)期為上千客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開(kāi)放共贏平臺(tái),與合作伙伴共同營(yíng)造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為綏棱企業(yè)提供專業(yè)的成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè),綏棱網(wǎng)站改版等技術(shù)服務(wù)。擁有10年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開(kāi)發(fā)。

ThreadLocal,即線程局部變量,用來(lái)為每一個(gè)使用它的線程維護(hù)一個(gè)獨(dú)立的變量副本。這種變量只在線程的生命周期內(nèi)有效。并且與鎖機(jī)制那種以時(shí)間換取空間的做法不同,ThreadLocal沒(méi)有任何鎖機(jī)制,它以空間換取時(shí)間的方式保證變量的線程安全。

本篇從源碼方面分析ThreadLocal的實(shí)現(xiàn)原理。

先看一下ThreadLocal類圖結(jié)構(gòu)

  ThreadLocal的實(shí)現(xiàn)原理是什么

SuppliedThreadLocal主要是JDK1.8用來(lái)擴(kuò)展對(duì)Lambda表達(dá)式的支持,有興趣的自行百度。

ThreadLocalMap是ThreadLocal的靜態(tài)內(nèi)部類,也是實(shí)際保存變量的類。

Entry是ThreadLocalMap的靜態(tài)內(nèi)部類。ThreadLocalMap持有一個(gè)Entry數(shù)組,以ThreadLocal為key,變量為value,封裝一個(gè)Entry。

下面以一張圖簡(jiǎn)要說(shuō)明Thread,ThreadLocal,ThreadLocalMap和Entry的關(guān)系。

  ThreadLocal的實(shí)現(xiàn)原理是什么

說(shuō)明一下上圖:

  1. 一個(gè)Thread擁有一個(gè)ThreadLocalMap對(duì)象

  2. ThreadLocalMap擁有一個(gè)Entry數(shù)組

  3. 每個(gè)Entry都有k--v

  4. Entry的key就是某個(gè)具體的ThreadLocal對(duì)象

下面分析主要方法。

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對(duì)象;具體存值調(diào)用的是ThreadLocalMap的set(),傳入的參數(shù)key就是當(dāng)前ThreadLocal對(duì)象。

再看看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();
        }
  1. 通過(guò)key的hashCode與數(shù)組容量 -1 取模,計(jì)算數(shù)組index

  2. 從當(dāng)前index開(kāi)始遍歷,清除key為null的無(wú)效Entry

  3. 將K-V封裝為Entry,并放入數(shù)組

  4. 判斷是否需要進(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;
        }
  1. 計(jì)算index

  2. 當(dāng)前index上的Entry不為空且key相同,直接返回

  3. 否則去相鄰index尋找

  4. 循環(huán)查找,發(fā)現(xiàn)無(wú)效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;
                }
            }
        }

處理方式和查找保存類似,刪除對(duì)應(yīng)Entry后都會(huì)去除key為null的無(wú)效元素。

注意

static class Entry extends WeakReference<ThreadLocal<?>> {}

ThreadLocal可能存在OOM問(wèn)題。因?yàn)門(mén)hreadLocalMap是使用ThreadLocal的弱引用作為key的,發(fā)生GC時(shí),key被回收,這樣我們就無(wú)法訪問(wèn)key為null的value元素,如果value本身是較大的對(duì)象,那么線程一直不結(jié)束的話,value就一直無(wú)法得到回收。特別是在我們使用線程池時(shí),線程是復(fù)用的,不會(huì)殺死線程,這樣ThreadLocal弱引用被回收時(shí),value不會(huì)被回收。

在使用ThreadLocal時(shí),線程邏輯代碼結(jié)束時(shí),必須顯示調(diào)用ThreadLocal.remove()方法。

感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享ThreadLocal的實(shí)現(xiàn)原理是什么內(nèi)容對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問(wèn)題就找創(chuàng)新互聯(lián),詳細(xì)的解決方法等著你來(lái)學(xué)習(xí)!

網(wǎng)站標(biāo)題:ThreadLocal的實(shí)現(xiàn)原理是什么-創(chuàng)新互聯(lián)
網(wǎng)址分享:http://bm7419.com/article38/cedppp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化虛擬主機(jī)、網(wǎng)站設(shè)計(jì)公司品牌網(wǎng)站制作、網(wǎng)站改版靜態(tài)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

網(wǎng)站建設(shè)網(wǎng)站維護(hù)公司