最近2018中文字幕在日韩欧美国产成人片_国产日韩精品一区二区在线_在线观看成年美女黄网色视频_国产精品一区三区五区_国产精彩刺激乱对白_看黄色黄大色黄片免费_人人超碰自拍cao_国产高清av在线_亚洲精品电影av_日韩美女尤物视频网站

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
如何使用Java多線程之ThreadLocal

這篇文章運(yùn)用簡(jiǎn)單易懂的例子給大家介紹如何使用Java多線程之ThreadLocal,代碼非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

成都創(chuàng)新互聯(lián)是一家朝氣蓬勃的網(wǎng)站建設(shè)公司。公司專注于為企業(yè)提供信息化建設(shè)解決方案。從事網(wǎng)站開(kāi)發(fā),網(wǎng)站制作,網(wǎng)站設(shè)計(jì),網(wǎng)站模板,微信公眾號(hào)開(kāi)發(fā),軟件開(kāi)發(fā),成都小程序開(kāi)發(fā),10年建站對(duì)茶樓設(shè)計(jì)等多個(gè)領(lǐng)域,擁有豐富的網(wǎng)站設(shè)計(jì)經(jīng)驗(yàn)。

在多線程環(huán)境下,訪問(wèn)非線程安全的變量時(shí)必須進(jìn)行線程同步,例如使用synchronized方式訪問(wèn)HashMap實(shí)例。但是同步訪問(wèn)會(huì)降低并發(fā)性,影響系統(tǒng)性能。這時(shí)候就可以用空間換時(shí)間,如果我們給每個(gè)線程都分配一個(gè)獨(dú)立的變量,就可以用非同步的方式使用非線程安全的變量,我們稱這種變量為線程局部變量。

顧名思義,線程局部變量是指每個(gè)線程都有一份屬于自己獨(dú)立的變量副本,不會(huì)像普通局部變量一樣可以被其他線程訪問(wèn)到。Java并沒(méi)有提供語(yǔ)言級(jí)的線程局部變量,而是在類庫(kù)里提供了線程局部變量的功能,也就是這次的主角ThreadLocal類。

ThreadLocal的使用

如何使用Java多線程之ThreadLocal

Java8版本的ThreadLocal有上圖所示的4個(gè)public方法和一個(gè)protected的方法,第一個(gè)方法用于返回初始值,默認(rèn)是null。第二個(gè)靜態(tài)方法withInitial(Supplier supplier)是Java8版本新添加的,后面三個(gè)實(shí)例方法則非常的簡(jiǎn)單。

在Java8之前,使用ThreadLocal時(shí)想要設(shè)置初始值時(shí)需要繼承ThreadLocal類覆蓋protected T initialValue()方法才行,例如:

ThreadLocal threadLocal = new ThreadLocal() {
    @Override
    protected Integer initialValue() {
        return 0;
    }
};

在Java8版本可以使用新添加的靜態(tài)方法withInitial(Supplier supplier),非常方便的設(shè)置初始值,例如:

ThreadLocal threadLocal = ThreadLocal.withInitial(() -> 0);

System.out.println(threadLocal.get());
threadLocal.set(16);
System.out.println(threadLocal.get());
threadLocal.remove();
System.out.println(threadLocal.get());

// 同一個(gè)線程的輸出
0
16
0
Process finished with exit code 0

ThreadLocal的原理

那么ThreadLocal是怎么實(shí)現(xiàn)線程局部變量的功能的呢?其實(shí)ThreadLocal的基本原理并沒(méi)有十分復(fù)雜。ThreadLocal在內(nèi)部定義了一個(gè)靜態(tài)類ThreadLocalMap,ThreadLocalMap的鍵為ThreadLocal對(duì)象,ThreadLocalMap的值就是ThreadLocal存儲(chǔ)的值,不過(guò)這個(gè)ThreadLocalMap是在Thread類里維護(hù)的。我們來(lái)看一下ThreadLocal的部分源碼:

    // ThreadLocal的set方法
    public void set(T value) {
        // 獲取當(dāng)前線程對(duì)象
        Thread t = Thread.currentThread();
        // 獲取Map
        ThreadLocalMap map = getMap(t);
        if (map != null)
            // 設(shè)置值
            map.set(this, value);
        else
            // 初始化Map
            createMap(t, value);
    }
    
    // ThreadLocal的createMap方法
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    
    // Thread類定義的實(shí)例域
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

可以看出ThreadLocal的核心實(shí)現(xiàn)就是ThreadLocalMap的實(shí)現(xiàn)了,ThreadLocalMap內(nèi)部聲明了一個(gè)Entry類來(lái)存儲(chǔ)數(shù)據(jù):

static class Entry extends WeakReference> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal k, Object v) {
        super(k);
        value = v;
    }
}

ThreadLocalMap的實(shí)現(xiàn)與HashMap的實(shí)現(xiàn)有相似的地方,比如同樣是使用數(shù)組存儲(chǔ)數(shù)據(jù)和自動(dòng)擴(kuò)容,不同的是hash算法與hash碰撞后的處理不一樣。

        // ThreadLocalMap的set方法
        private void set(ThreadLocal key, Object value) {

            Entry[] tab = table;
            int len = tab.length;
            // 計(jì)算在Entry[]中的索引,每個(gè)ThreadLocal對(duì)象都有一個(gè)hash值threadLocalHashCode,每初始化一個(gè)ThreadLocal對(duì)象,hash值就增加一個(gè)固定的大小0x61c88647
            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;
                }
                // 代替無(wú)效的鍵
                if (k == null) {
                    replaceStaleEntry(key, value, i);
                    return;
                }
            }

            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }
        
        private static int nextIndex(int i, int len) {
            return ((i + 1 < len) ? i + 1 : 0);
        }

可以看到ThreadLocalMap把Entry[]數(shù)組當(dāng)成一個(gè)圓環(huán)。從計(jì)算出來(lái)的索引位置開(kāi)始,如果該索引已經(jīng)有數(shù)據(jù)了就判斷Key是否相同,相同就更新值。否則就直到找到一個(gè)空的位置把值放進(jìn)去。獲取值的時(shí)候也類似,從計(jì)算出來(lái)的索引位置開(kāi)始一個(gè)一個(gè)檢查Key是否相同,這樣hash碰撞比較多的話可能性能就不是很好。

ThreadLocal的應(yīng)用

ThreadLocal的應(yīng)用是非常廣的,比如Java工程師非常熟悉的Spring框架中就使用了ThreadLocal來(lái)把非線程安全的狀態(tài)性對(duì)象封裝起來(lái),所以我們可以把絕大部分的Bean聲明為singleton作用域。我們?cè)诰帉懚嗑€程代碼時(shí)也可以想想是用同步的方式訪問(wèn)非線程安全的狀態(tài)性對(duì)象比較好,還是使用ThreadLocal把非線程安全的狀態(tài)性對(duì)象封裝起來(lái)更好。

關(guān)于如何使用Java多線程之ThreadLocal就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。


分享標(biāo)題:如何使用Java多線程之ThreadLocal
分享網(wǎng)址:http://fisionsoft.com.cn/article/pdgedh.html