最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java線程中的ThreadLocal類解讀

 更新時(shí)間:2023年11月28日 10:30:22   作者:后仰大風(fēng)車  
這篇文章主要介紹了Java線程中的ThreadLocal類解讀,ThreadLocal是一個(gè)泛型類,作用是實(shí)現(xiàn)線程隔離,ThreadLocal類型的變量,在每個(gè)線程中都會(huì)對(duì)應(yīng)一個(gè)具體對(duì)象,對(duì)象類型需要在聲明ThreadLocal變量時(shí)指定,需要的朋友可以參考下

Java的ThreadLocal類

ThreadLocal是一個(gè)泛型類,作用是實(shí)現(xiàn)線程隔離,ThreadLocal類型的變量,在每個(gè)線程中都會(huì)對(duì)應(yīng)一個(gè)具體對(duì)象,對(duì)象類型需要在聲明ThreadLocal變量時(shí)指定。

ThreadLocal的使用示例

package org.example.thread;

import org.example.domain.Book;

public class ThreadLocalTest {

    ThreadLocal<Book> localBook = new ThreadLocal<>();

    Book book = new Book();

    public void test() {

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                localBook.set(book);
                System.out.println(localBook.get());
                localBook.remove();
            }
        });
        thread.start();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println(localBook.get());

        localBook.remove();
    }

    public static void main(String[] arg) {
        ThreadLocalTest localBookTest = new ThreadLocalTest();
        localBookTest.test();
    }
}

運(yùn)行結(jié)果如下:

在這里插入圖片描述

示例代碼中,只聲明了一個(gè) ThreadLocal 變量localBook,在子線程中設(shè)置了localBook的值,但是在最后主線程中進(jìn)行打印時(shí),發(fā)現(xiàn)為null,和子線程中的結(jié)果不一樣。

具體原因還要看看ThreadLocal類,以及內(nèi)部 set、get等方法的實(shí)現(xiàn)。

原理

1、ThreadLocal中有一個(gè)靜態(tài)內(nèi)部類 ThreadLocalMap,ThreadLocalMap中維護(hù)了一個(gè) Entry數(shù)組,Entry又是ThreadLocalMap的內(nèi)部類,用來表示一個(gè)KV鍵值對(duì)。部分源碼如下:

    static class ThreadLocalMap {

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

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

        private static final int INITIAL_CAPACITY = 16;

        private Entry[] table;

Entry的 key是一個(gè)指向ThreadLocal對(duì)象的弱引用,value則指向與key對(duì)應(yīng)的Object對(duì)象。

2、Thread類中,有一個(gè)ThreadLocalMap類型的成員變量,初始為null:

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;

也就是說,每個(gè)線程對(duì)象中,都有一個(gè)ThreadLocalMap,它里面可以有很多個(gè)Entry,每個(gè)Entry中的key都指向一個(gè)ThreadLocal對(duì)象,value則指向與key相對(duì)應(yīng)的Object對(duì)象。在線程中調(diào)用ThreadLocal對(duì)象的set、get方法,實(shí)際操作的是當(dāng)前線程自己的ThreadLocalMap。

3、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);
        }
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

果然,這里先拿到當(dāng)前線程中的ThreadLocalMap,如果不為空則直接添加一組鍵值對(duì),key是當(dāng)前的ThreadLocal變量; 如果map為空,則新建一個(gè)map,同樣添加一組鍵值對(duì),key是當(dāng)前的ThreadLocal變量,然后將map賦值給當(dāng)前線程的ThreadLocalMap。

4、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();
    }

同理,get方法拿到的也是線程本地的ThreadLocalMap中,與當(dāng)前ThreadLocal變量對(duì)應(yīng)的value對(duì)象。

5、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;
                }
            }
        }

remove方法,移除當(dāng)前線程的ThreadLocalMap中,與當(dāng)前ThreadLocal對(duì)象對(duì)應(yīng)的鍵值對(duì)(key、value及Entry置空)。

內(nèi)存泄漏相關(guān)

1、為什么ThreadLocalMap中的key要使用弱引用?

弱引用特性:在對(duì)象沒有被強(qiáng)引用指向,而僅被弱引用指向的情況下,發(fā)生垃圾回收時(shí),會(huì)直接清理掉該對(duì)象。 套用網(wǎng)上出現(xiàn)較多的一張圖片來表示ThreadLocal變量的內(nèi)存結(jié)構(gòu):

在這里插入圖片描述

這里實(shí)線表示強(qiáng)引用,虛線表示弱引用。 假設(shè)key也是強(qiáng)引用,如果我們把 ThreadLocal Ref這個(gè)引用置為null,表示我們不再需要這個(gè)ThreadLocal對(duì)象,那么發(fā)生垃圾回收時(shí),這個(gè)變量理應(yīng)被回收掉;但實(shí)際并非如此,因?yàn)楫?dāng)前線程中還持有一個(gè)強(qiáng)引用,也就是ThreadLocalMap中的key還在指向它,那么只要線程不結(jié)束,這個(gè)沒有實(shí)際作用的ThreadLocal對(duì)象就一直不會(huì)被回收,從而出現(xiàn)內(nèi)存泄漏。而將key設(shè)計(jì)為弱引用,就能保證這種情況下ThreadLocal對(duì)象被回收,一定程度避免內(nèi)存泄漏問題。

2、為什么使用完ThreadLocal對(duì)象,要在線程中調(diào)用remove方法? 雖然弱引用可以使不再使用的ThreadLocal對(duì)象被回收掉,但還有一個(gè)問題: Entry中的key被置為了null,對(duì)應(yīng)的value已經(jīng)無法通過key訪問到,然而Current thread -> ThreadLocalMap -> Entry(value) -> my value 這條強(qiáng)引用鏈仍然存在,也就是說還存在key為null,但value不為null的entry,如果這類entry不被清理掉,還是會(huì)導(dǎo)致內(nèi)存泄露。

key為null的entry如何清理? 在ThreadLocalMap的Entry數(shù)組中,key為null的entry也會(huì)占用一個(gè)數(shù)組下標(biāo),而Threadlocal的set、get等方法,正是根據(jù)key的hashCode計(jì)算得到數(shù)組下標(biāo),然后根據(jù)下標(biāo)找到對(duì)應(yīng)的entry進(jìn)行操作。 為了維護(hù)ThreadLocalMap的可用性,不讓這些key為null的無用entry占用過多空間,set、get方法在某些情況下也會(huì)對(duì)key為null的entry進(jìn)行清理。但是用戶有可能不再需要調(diào)用其他ThreadLocal變量的set或get方法,所以這種方式是被動(dòng)且沒有針對(duì)性的。

建議在不使用ThreadLocal對(duì)象之后,直接調(diào)用remove方法,其作用就是將對(duì)應(yīng)entry的key、value置空,并將entry從數(shù)組中移除,切斷下圖中的這三處引用關(guān)系,防止內(nèi)存泄漏。

在這里插入圖片描述

到此這篇關(guān)于Java線程中的ThreadLocal類解讀的文章就介紹到這了,更多相關(guān)Java的ThreadLocal類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java中springMVC獲取請(qǐng)求參數(shù)的方法

    java中springMVC獲取請(qǐng)求參數(shù)的方法

    這篇文章主要介紹了java中springMVC獲取請(qǐng)求參數(shù)的方法,springmvc是spring框架的一個(gè)模塊,springmvc和spring無需通過中間整合層進(jìn)行整合,需要的朋友可以參考下
    2023-05-05
  • Java TreeSet實(shí)現(xiàn)學(xué)生按年齡大小和姓名排序的方法示例

    Java TreeSet實(shí)現(xiàn)學(xué)生按年齡大小和姓名排序的方法示例

    這篇文章主要介紹了Java TreeSet實(shí)現(xiàn)學(xué)生按年齡大小和姓名排序的方法,涉及java類型轉(zhuǎn)換、遍歷、比較等相關(guān)操作技巧,需要的朋友可以參考下
    2017-09-09
  • Java設(shè)置token有效期的5個(gè)應(yīng)用場(chǎng)景(雙token實(shí)現(xiàn))

    Java設(shè)置token有效期的5個(gè)應(yīng)用場(chǎng)景(雙token實(shí)現(xiàn))

    Token最常見的應(yīng)用場(chǎng)景之一就是身份驗(yàn)證,本文主要介紹了Java設(shè)置token有效期的5個(gè)應(yīng)用場(chǎng)景(雙token實(shí)現(xiàn)),具有一定的參考價(jià)值,感興趣的可以來了解一下
    2024-04-04
  • MyBatis連接池的深入和動(dòng)態(tài)SQL詳解

    MyBatis連接池的深入和動(dòng)態(tài)SQL詳解

    這篇文章主要介紹了MyBatis連接池的深入和動(dòng)態(tài)SQL詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 在Eclipse安裝Spring boot插件的步驟(圖文)

    在Eclipse安裝Spring boot插件的步驟(圖文)

    這篇文章主要介紹了在Eclipse安裝Spring boot插件的步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-08-08
  • Java中生產(chǎn)者消費(fèi)者問題總結(jié)

    Java中生產(chǎn)者消費(fèi)者問題總結(jié)

    這篇文章主要介紹了Java中生產(chǎn)者消費(fèi)者問題總結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • java模擬多線程http請(qǐng)求代碼分享

    java模擬多線程http請(qǐng)求代碼分享

    本篇文章給大家分享了java模擬多線程http請(qǐng)求的相關(guān)實(shí)例代碼,對(duì)此有需要的可以跟著測(cè)試下。
    2018-05-05
  • 最新評(píng)論

    乌什县| 苍南县| 海阳市| 淮滨县| 安新县| 揭西县| 额敏县| 北碚区| 阿拉尔市| 晋州市| 保康县| 扎兰屯市| 古蔺县| 阿克| 巨野县| 丹巴县| 临城县| 吴江市| 电白县| 永川市| 洪湖市| 宜丰县| 密山市| 普陀区| 娱乐| 区。| 同江市| 德令哈市| 青冈县| 江华| 鞍山市| 彭阳县| 应城市| 新巴尔虎右旗| 华安县| 手机| 潼南县| 新沂市| 西安市| 文成县| 德令哈市|