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

Java中的ThreadLocal源碼及弱引用解析

 更新時(shí)間:2024年01月11日 10:35:45   作者:好奇的7號(hào)  
這篇文章主要介紹了Java中的ThreadLocal源碼及弱引用解析,ThreadLocal類通過(guò)ThreadLocal可以實(shí)現(xiàn)全局變量在多線程環(huán)境下的線程隔離,每個(gè)線程都可以獨(dú)立地訪問(wèn)和修改自己的全局變量副本,不會(huì)影響其他線程的副本,需要的朋友可以參考下

引言

我們知道在通常情況下,對(duì)于主存中的變量,每一個(gè)線程都能夠訪問(wèn)并修改該變量(或?qū)ο螅?/p>

與之相對(duì)的,如果我們需要實(shí)現(xiàn)每個(gè)線程擁有自己專屬的本地變量,該如何操作呢?

此時(shí)引出ThreadLocal類,通過(guò)ThreadLocal可以實(shí)現(xiàn)全局變量在多線程環(huán)境下的線程隔離。

每個(gè)線程都可以獨(dú)立地訪問(wèn)和修改自己的全局變量副本,不會(huì)影響其他線程的副本。

這在某些場(chǎng)景下可以簡(jiǎn)化代碼的編寫(xiě)和理解。

源碼解析

示例代碼

我們先從一段簡(jiǎn)單的代碼示例入手:

package Thread_;
 
public class ThreadLocal {
    private static java.lang.ThreadLocal<Integer> counter = new java.lang.ThreadLocal<>();
 
    public static void main(String[] args) {
        Runnable runnable = () -> {
            // 獲取當(dāng)前線程的計(jì)數(shù)器值,初始值為0
            int count = counter.get() == null ? 0 : counter.get();
            System.out.println(Thread.currentThread().getName() + " 的計(jì)數(shù)器值為: " + count);
 
            // 對(duì)計(jì)數(shù)器進(jìn)行累加操作
            counter.set(count + 1);
 
            // 模擬耗時(shí)操作
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
 
            // 再次獲取計(jì)數(shù)器值
            count = counter.get();
            System.out.println(Thread.currentThread().getName() + " 的累加后計(jì)數(shù)器值為: " + count);
        };
 
        // 創(chuàng)建三個(gè)線程并啟動(dòng)
        Thread thread1 = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        Thread thread3 = new Thread(runnable);
 
        thread1.start();
        thread2.start();
        thread3.start();
    }
}
 

//結(jié)果如下:
Thread-1 的計(jì)數(shù)器值為: 0
Thread-0 的計(jì)數(shù)器值為: 0
Thread-2 的計(jì)數(shù)器值為: 0
Thread-1 的累加后計(jì)數(shù)器值為: 1
Thread-2 的累加后計(jì)數(shù)器值為: 1
Thread-0 的累加后計(jì)數(shù)器值為: 1

在代碼中,我們定義了一個(gè)ThreadLocal類的整形對(duì)象counter,用三個(gè)線程進(jìn)行累加操作。

結(jié)果我們發(fā)現(xiàn),counter的值并沒(méi)有變?yōu)?,而是每個(gè)線程有一個(gè)自己的counter值,分別為1。

由此引出ThreadLoca的作用:

ThreadLocal對(duì)象在每個(gè)線程內(nèi)是共享的,在不同線程之間又是隔離的(每個(gè)線程都只能看到自己獨(dú)有的ThreadLocal對(duì)象的值),即,實(shí)現(xiàn)了線程范圍內(nèi)的局部變量的作用。

線程獨(dú)享原因

源碼解析

首先我們可以推測(cè),如果要保證每個(gè)線程獨(dú)享一份數(shù)據(jù),那這份數(shù)據(jù)應(yīng)該要能夠從線程內(nèi)部進(jìn)行引用。

實(shí)際上,線程的棧中存放了ThreadLocal.ThreadLocalMap這么一個(gè)屬性(初始化為空),ThreadLocalMap是ThreadLocal的一個(gè)靜態(tài)內(nèi)部類,以后數(shù)據(jù)就要以ThreadLocalMap的形式在堆中實(shí)例化,并讓threadLocals成為它的引用!這樣就完成了線程的獨(dú)享了。

public class Thread implements Runnable{
    ...
    ...
    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
    ...
}

我們回到示例中,注意:ThreadLocal<Integer> counter這個(gè)ThreadLocal對(duì)象的set方法,跟進(jìn)源碼,詳細(xì)解釋如下:

// 對(duì)計(jì)數(shù)器進(jìn)行累加操作
counter.set(count + 1);
 
//源碼1. set源碼:
public void set(T value) {
        Thread t = Thread.currentThread();//獲取當(dāng)前線程
        ThreadLocalMap map = getMap(t);//讓map成為該線程內(nèi)的threadLocals所引用的堆中的對(duì)象!
//見(jiàn)源碼2. 
//其實(shí)就是去嘗試引用實(shí)例化的ThreadLocalMap,但此時(shí)初始化為null,所以我們看下面的判斷:
 
        if (map != null) {
            map.set(this, value);//不為空,已經(jīng)有map,就把堆中的值賦值為counter,count + 1
        } else {
            createMap(t, value);//為空,創(chuàng)建ThreadLocalMap的對(duì)象
            //但要注意,createMap并不是讓map實(shí)例化,見(jiàn)下面源碼3.
        }
    }
 
//源碼2. getMap源碼:
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    //意思就是,返回這個(gè)t線程的ThreadLocal.ThreadLocalMap threadLocals = null里面這個(gè)threadLocals 對(duì)象
    }
 
//源碼3. createMap源碼:
//傳入的this,其實(shí)就是counter。firstValue就是相應(yīng)的值count + 1。
//注意,是將t.threadLocals線程內(nèi)的這個(gè)ThreadLocalMap對(duì)象實(shí)例化,所以線程內(nèi)部的這個(gè)對(duì)象指向了堆中內(nèi)存的new...,實(shí)現(xiàn)了線程內(nèi)部的數(shù)據(jù)獨(dú)立。
void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

概括:

看似是向ThreadLocal存入一個(gè)值,實(shí)際上是向當(dāng)前線程對(duì)象中的ThreadLocalMap對(duì)象存入值(如果為空,則實(shí)例化當(dāng)前線程對(duì)象中的ThreadLocalMap對(duì)象)ThreadLocalMap我們可以簡(jiǎn)單的理解成一個(gè)Map,Map存的key就是ThreadLocal實(shí)例本身(counter),value是具體的值。

get方法同理,也是先取出當(dāng)前線程對(duì)象,再取出其指向的map里的值:

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

1.3 ThreadLocalMap的key的弱引用

源碼如下:

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

通常ThreadLocalMap的生命周期跟Thread一樣長(zhǎng),如果沒(méi)有手動(dòng)刪除對(duì)應(yīng)key會(huì)導(dǎo)致內(nèi)存泄漏,但是使用弱引用可以多一層保障:弱引用ThreadLocal會(huì)被GC回收,不會(huì)內(nèi)存泄漏,對(duì)應(yīng)的value在下一次ThreadLocalMap調(diào)用set,get,remove的時(shí)候會(huì)被清除。

ThreadLocal中一個(gè)設(shè)計(jì)亮點(diǎn)是ThreadLocalMap中的Entry結(jié)構(gòu)的Key用到了弱引用。 試想如果使用強(qiáng)引用,如果ThreadLocalMap的Key使用強(qiáng)引用,那么Key對(duì)應(yīng)的ThreadLocal對(duì)象在沒(méi)有被外部引用時(shí)仍然無(wú)法被GC回收,因?yàn)镵ey存在于ThreadLocalMap中,而且線程是長(zhǎng)時(shí)間存活的。這就可能導(dǎo)致ThreadLocal對(duì)象無(wú)法被回收,從而造成內(nèi)存泄漏。

使用了弱引用的話,JVM觸發(fā)GC回收弱引用后,ThreadLocalMap中會(huì)出現(xiàn)一些Key為null,但是Value不為null的Entry項(xiàng),這些Entry項(xiàng)如果不主動(dòng)清理,就會(huì)一直駐留在ThreadLocalMap中。此時(shí),ThreadLocal在下一次調(diào)用get()、set()、remove()方法就可以刪除那些ThreadLocalMap中Key為null的值,起到了惰性刪除釋放內(nèi)存的作用。

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

相關(guān)文章

  • springboot自動(dòng)裝配原理初識(shí)

    springboot自動(dòng)裝配原理初識(shí)

    這篇文章主要介紹了springboot自動(dòng)裝配原理的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)使用springboot,感興趣的朋友可以了解下
    2021-04-04
  • java圖論弗洛伊德和迪杰斯特拉算法解決最短路徑問(wèn)題

    java圖論弗洛伊德和迪杰斯特拉算法解決最短路徑問(wèn)題

    這篇文章主要為大家介紹了java圖論弗洛伊德算法和迪杰斯特拉算法解決最短路徑的問(wèn)題示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2021-11-11
  • 詳解mybatis三種分頁(yè)方式

    詳解mybatis三種分頁(yè)方式

    本文主要介紹了詳解mybatis三種分頁(yè)方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • Java前端開(kāi)發(fā)之HttpServletRequest的使用

    Java前端開(kāi)發(fā)之HttpServletRequest的使用

    service方法中的request的類型是ServletRequest,而doGet/doPost方法的request的類型是HttpServletRequest,HttpServletRequest是ServletRequest的子接口,功能和方法更加強(qiáng)大
    2023-01-01
  • 使用Maven打包時(shí)排除指定的目錄或指定的類

    使用Maven打包時(shí)排除指定的目錄或指定的類

    在maven項(xiàng)目中,通過(guò)修改pom.xml文件,在maven-compiler-plugin的configuration>excludes節(jié)點(diǎn)下添加排除匹配條件,可以剔除掉生產(chǎn)包中的測(cè)試類,注意使用**表示多級(jí)目錄匹配,*表示一級(jí)目錄匹配
    2024-11-11
  • 我對(duì)@RestController注解的理解

    我對(duì)@RestController注解的理解

    這篇文章主要介紹了我對(duì)@RestController注解的理解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • IDEA配置文件application.properties亂碼問(wèn)題及解決過(guò)程

    IDEA配置文件application.properties亂碼問(wèn)題及解決過(guò)程

    在IntelliJ IDEA中打開(kāi)application.properties時(shí),中文注釋出現(xiàn)亂碼,原因多為文件編碼設(shè)置不正確,即使顯示UTF-8仍可能亂碼,解決方法是設(shè)置屬性文件默認(rèn)編碼為UTF-8,保存后注釋可正常顯示
    2025-09-09
  • 輕松掌握java組合模式

    輕松掌握java組合模式

    這篇文章主要幫助大家輕松掌握java組合模式,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2016-09-09
  • java寫(xiě)入文件的幾種方法分享

    java寫(xiě)入文件的幾種方法分享

    這篇文章主要介紹了java寫(xiě)入文件的幾種方法,需要的朋友可以參考下
    2014-02-02
  • Java圖形化界面設(shè)計(jì)之容器(JFrame)詳解

    Java圖形化界面設(shè)計(jì)之容器(JFrame)詳解

    這篇文章主要介紹了Java圖形化界面設(shè)計(jì)之容器(JFrame)詳解,條理清晰,依次介紹了Java基本類(JFC),AWT和Swing的區(qū)別,Swing基本框架,圖形化設(shè)計(jì)步驟以及組件容器的使用等相關(guān)內(nèi)容,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11

最新評(píng)論

汉沽区| 利辛县| 乌拉特中旗| 临桂县| 临西县| 和林格尔县| 永宁县| 共和县| 宁乡县| 德令哈市| 博野县| 靖江市| 泾阳县| 临猗县| 徐汇区| 永康市| 西畴县| 吉木萨尔县| 多伦县| 威信县| 靖边县| 福泉市| 万载县| 南岸区| 林周县| 东乡县| 大名县| 恩平市| 博罗县| 汝州市| 确山县| 福海县| 任丘市| 屯门区| 美姑县| 七台河市| 木兰县| 岱山县| 林周县| 应用必备| 湛江市|