欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java中ThreadLocal的基本原理

 更新時(shí)間:2021年08月25日 15:40:37   作者:summer_west_fish  
本文講解了java中ThreadLocal的一些基本原理,文中關(guān)于ThreadLocal的原理講解的非常詳細(xì),感興趣的朋友一起看看吧

源碼實(shí)現(xiàn)

一個(gè)線程內(nèi)可以存多個(gè)ThreadLocal對象,存儲的位置位于Thread的ThreadLocal.ThreadLocalMap變量,在Thread中有如下變量:

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

ThreadLocalMap是由ThreadLocal維護(hù)的靜態(tài)內(nèi)部類,正如代碼中注解所說這個(gè)變量是由ThreadLocal維護(hù)的。

基本流程

在這里插入圖片描述

ThreadLoalMap數(shù)據(jù)結(jié)構(gòu)

ThreadLoalMap是ThreadLocal中的一個(gè)靜態(tài)內(nèi)部類,類似HashMap的數(shù)據(jù)結(jié)構(gòu),但并沒有實(shí)現(xiàn)Map接口。

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

ThreadLoalMap中初始化了一個(gè)大小16的Entry數(shù)組,Entry對象用來保存每一個(gè)key-value鍵值對。通過上面的set方法,我們已經(jīng)知道其中的key永遠(yuǎn)都是ThreadLocal對象。

在這里插入圖片描述

Hash沖突及解決

Entry在table中存儲位置是通過hashcode算法獲得。
在向ThreadLocalMap中的Entry數(shù)值存儲Entry對象時(shí),會根據(jù)ThreadLocal對象的hash值,定位到table中的位置i。分三種情況:

  • 如果當(dāng)前位置為空的,直接將Entry存放在對應(yīng)位置;
  • 如果位置i已經(jīng)有值且這個(gè)Entry對象的key正好是即將設(shè)置的key,那么重新設(shè)置Entry中的value;
  • 如果位置i的Entry對象和即將設(shè)置的key沒關(guān)系,則尋找一個(gè)空位置;

計(jì)算hash值便會有hash沖突出現(xiàn),常見的解決方法有:再哈希法、開放地址法、建立公共溢出區(qū)、鏈?zhǔn)降刂贩ǖ取?/p>

上面的流程可以看出這里采用的是開放地址方法,如果當(dāng)前位置有值,就繼續(xù)尋找下一個(gè)位置,注意table[len-1]的下一個(gè)位置是table[0],就像是一個(gè)環(huán)形數(shù)組,所以也叫閉散列法。 如果一直都找不到空位置就會出現(xiàn)死循環(huán),發(fā)生內(nèi)存溢出。當(dāng)然有擴(kuò)容機(jī)制,一般不會找不到空位置的。

ThreadLocal內(nèi)存泄露

內(nèi)存引用鏈路

根據(jù)前面對ThreadLocal的分析,得知每個(gè)Thread維護(hù)一個(gè)ThreadLocalMap,它key是ThreadLocal實(shí)例本身,value是業(yè)務(wù)需要存儲的Object。也就是說ThreadLocal本身并不存儲值,它只是作為一個(gè)key來讓線程從ThreadLocalMap獲取value。

ThreadLocalMap是使用ThreadLocal的弱引用作為Key的,弱引用的對象在GC時(shí)會被回收。因此使用了ThreadLocal后,引用鏈如圖所示:(其中虛線表示弱引用。)

在這里插入圖片描述

引用類型

強(qiáng)引用:java默認(rèn)的引用類型,例如 Object a = new Object();其中 a 為強(qiáng)引用,new Object()為一個(gè)具體的對象。一個(gè)對象從根路徑能找到強(qiáng)引用指向它,jvm虛擬機(jī)就不會回收。

軟引用(SoftReference):進(jìn)行年輕代的垃圾回收不會觸發(fā)SoftReference所指向?qū)ο蟮幕厥眨?strong>但如果觸發(fā)Full GC,那SoftReference所指向的對象將被回收。備注:是除了軟引用之外沒有其他強(qiáng)引用引用的情況下。

弱引用(WeakReference) :如果對象除了有弱引用指向它后沒有其他強(qiáng)引用關(guān)聯(lián)它,當(dāng)進(jìn)行年輕代垃圾回收時(shí),該引用指向的對象就會被垃圾回收器回收。

虛引用(PhantomeReference) :該引用指向的對象,無法對垃圾收集器收集對象時(shí)產(chǎn)生任何影響,但在執(zhí)行垃圾回收后垃圾收集器會通過注冊在PhantomeReference上的隊(duì)列來通知應(yīng)用程序?qū)ο蟊换厥铡?/strong>

為什么使用弱引用而不是強(qiáng)引用?

問題1:從表面上看內(nèi)存泄漏的根源在于使用了弱引用,但為什么JDK采用了弱引用的實(shí)現(xiàn)而不是強(qiáng)引用呢?

答案是:弱引用反而是為了解決內(nèi)存存儲問題而專門使用的。

問題2:如果應(yīng)用程序覺得ThreadLocal對象的使命完成,將threadLocal ref 設(shè)置為null,如果Entry中引用ThreadLocald對象的引用類型設(shè)置為強(qiáng)引用的話,會發(fā)生什么問題?

答案是:ThreadLocal對象會無法被垃圾回收器回收,因?yàn)閺膖hread對象出發(fā),有強(qiáng)引用指向ThreadLocal的object。此時(shí)會違背用戶的初衷,造成所謂的內(nèi)存泄露。

我們先來假設(shè)一下,如果key使用強(qiáng)引用,那么在其他持有ThreadLocal引用的對象都回收了,但ThreadLocalMap依舊持有ThreadLocal的強(qiáng)引用,這就導(dǎo)致ThreadLocal不會被回收,從而導(dǎo)致Entry內(nèi)存泄露。

對照一下,弱引用的情況。持有ThreadLocal引用的對象都回收了,ThreadLocalMap持有的是ThreadLocal的弱引用,會被自動回收。只不過對應(yīng)的value值,需要在下次調(diào)用set/get/remove方法時(shí)會被清除。

泄露原因分析

當(dāng)Thread執(zhí)行完會被銷毀,Thread.threadLocals指向的ThreadLocalMap實(shí)例也隨之變?yōu)槔锩娲娣诺腅ntity也會被回收。這種情況是不會發(fā)生內(nèi)存泄漏的。

發(fā)生內(nèi)存泄露的場景一般存在于線程池的情況下。 此時(shí),Thread生命周期比較長(存在循環(huán)使用),threadLocals引用一直存在,當(dāng)其存放的ThreadLocal被回收(弱引用生命周期比較短)后,對應(yīng)的Entity就成了key為null的實(shí)例,但value值不會被回收。 如果此Entity一直不被get()、set()、remove(),就一直不會被回收,也就發(fā)生了內(nèi)存泄漏。

所以,通常在使用完ThreadLocal后需要調(diào)用remove()方法進(jìn)行內(nèi)存的清除。

接下來我們再延伸一下,想再來談?wù)劸W(wǎng)絡(luò)上關(guān)于ThreadLocalMap中存儲大量Entry對象導(dǎo)致的內(nèi)存“泄露”問題?

網(wǎng)絡(luò)觀點(diǎn):在使用ThreadLocal中set方法與remove方法需要成對執(zhí)行,需要沒有執(zhí)行remove方法會造成內(nèi)存泄露?甚至造成內(nèi)存溢出?

我的觀點(diǎn):當(dāng)然能成對使用當(dāng)然更好,但在實(shí)際情況中,其實(shí)不調(diào)用remove方法也不太容易造成內(nèi)存溢出,因?yàn)閺拇鎯Y(jié)構(gòu)來看,除非創(chuàng)建海量線程,并且這些線程都不釋放,導(dǎo)致大量線程內(nèi)部持有的ThreadLocalMap中對象一直不會釋放,但一個(gè)線程所持有的Entry對象個(gè)數(shù)不多,取決于關(guān)聯(lián)的ThreadLocal對象個(gè)數(shù),故我們需要的關(guān)注點(diǎn)而不是remove方法,而是防止線程資源泄露。

ThreadLocal應(yīng)用場景

  • 線程間數(shù)據(jù)隔離,各線程的ThreadLocal互不影響;
  • 方便同一個(gè)線程使用某一對象,避免不必要的參數(shù)傳遞;
  • 全鏈路追蹤中的traceId或者流程引擎中上下文的傳遞一般采用ThreadLocal;
  • Spring事務(wù)管理器采用了ThreadLocal;
  • Spring MVC的RequestContextHolder的實(shí)現(xiàn)使用了ThreadLocal;

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

相關(guān)文章

  • java8根據(jù)某一屬性過濾去重的實(shí)例

    java8根據(jù)某一屬性過濾去重的實(shí)例

    這篇文章主要介紹了java8根據(jù)某一屬性過濾去重的實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-05-05
  • 用3個(gè)實(shí)例從原理到實(shí)戰(zhàn)講清楚Log4j史詩級漏洞

    用3個(gè)實(shí)例從原理到實(shí)戰(zhàn)講清楚Log4j史詩級漏洞

    最近應(yīng)該很多人都在關(guān)注著一個(gè)漏洞Apache Log4j 2遠(yuǎn)程代碼執(zhí)行,該漏洞一旦被攻擊者利用會造成嚴(yán)重危害,這篇文章主要給大家介紹了關(guān)于如何用3個(gè)實(shí)例從原理到實(shí)戰(zhàn)講清楚Log4j史詩級漏洞的相關(guān)資料,需要的朋友可以參考下
    2021-12-12
  • Java線程池execute()方法源碼全面解析

    Java線程池execute()方法源碼全面解析

    這篇文章主要介紹了Java線程池execute()方法源碼全面解析,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Java Spring @Lazy延遲注入源碼案例詳解

    Java Spring @Lazy延遲注入源碼案例詳解

    這篇文章主要介紹了Java Spring @Lazy延遲注入源碼案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • Java 使用 HttpClient 發(fā)送 GET請求和 POST請求

    Java 使用 HttpClient 發(fā)送 GET請求和 POST請求

    本文主要介紹了Java 使用 HttpClient 發(fā)送 GET請求和 POST請求,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • 理解Java的序列化與反序列化

    理解Java的序列化與反序列化

    這篇文章主要為大家詳細(xì)介紹了Java的序列化與反序列化,序列化是一種對象持久化的手段。普遍應(yīng)用在網(wǎng)絡(luò)傳輸、RMI等場景中。本文通過分析ArrayList的序列化來介紹Java序列化的相關(guān)內(nèi)容,感興趣的小伙伴們可以參考一下
    2016-02-02
  • java編程Reference核心原理示例源碼分析

    java編程Reference核心原理示例源碼分析

    這篇文章主要為大家介紹了java編程Reference的核心原理以及示例源碼分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-01-01
  • Java鍵值對Pair的使用方式和操作實(shí)現(xiàn)

    Java鍵值對Pair的使用方式和操作實(shí)現(xiàn)

    鍵值對是一種常見的數(shù)據(jù)結(jié)構(gòu),它由一個(gè)唯一的鍵和與之關(guān)聯(lián)的值組成,本文就來介紹一下Java鍵值對Pair的使用方式和操作實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12
  • Java 數(shù)組元素倒序的三種方式(小結(jié))

    Java 數(shù)組元素倒序的三種方式(小結(jié))

    這篇文章主要介紹了Java 數(shù)組元素倒序的三種方式(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • springboot+log4j.yml配置日志文件的方法

    springboot+log4j.yml配置日志文件的方法

    這篇文章主要介紹了springboot+log4j.yml配置日志文件的方法,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-02-02

最新評論