亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

java中ThreadLocal的基本原理

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

源碼實現(xiàn)

一個線程內可以存多個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維護的靜態(tài)內部類,正如代碼中注解所說這個變量是由ThreadLocal維護的。

基本流程

在這里插入圖片描述

ThreadLoalMap數(shù)據(jù)結構

ThreadLoalMap是ThreadLocal中的一個靜態(tài)內部類,類似HashMap的數(shù)據(jù)結構,但并沒有實現(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中初始化了一個大小16的Entry數(shù)組,Entry對象用來保存每一個key-value鍵值對。通過上面的set方法,我們已經知道其中的key永遠都是ThreadLocal對象。

在這里插入圖片描述

Hash沖突及解決

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

  • 如果當前位置為空的,直接將Entry存放在對應位置;
  • 如果位置i已經有值且這個Entry對象的key正好是即將設置的key,那么重新設置Entry中的value;
  • 如果位置i的Entry對象和即將設置的key沒關系,則尋找一個空位置;

計算hash值便會有hash沖突出現(xiàn),常見的解決方法有:再哈希法、開放地址法、建立公共溢出區(qū)、鏈式地址法等。

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

ThreadLocal內存泄露

內存引用鏈路

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

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

在這里插入圖片描述

引用類型

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

軟引用(SoftReference):進行年輕代的垃圾回收不會觸發(fā)SoftReference所指向對象的回收;但如果觸發(fā)Full GC,那SoftReference所指向的對象將被回收。備注:是除了軟引用之外沒有其他強引用引用的情況下。

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

虛引用(PhantomeReference) :該引用指向的對象,無法對垃圾收集器收集對象時產生任何影響,但在執(zhí)行垃圾回收后垃圾收集器會通過注冊在PhantomeReference上的隊列來通知應用程序對象被回收。

為什么使用弱引用而不是強引用?

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

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

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

答案是:ThreadLocal對象會無法被垃圾回收器回收,因為從thread對象出發(fā),有強引用指向ThreadLocal的object。此時會違背用戶的初衷,造成所謂的內存泄露。

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

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

泄露原因分析

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

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

所以,通常在使用完ThreadLocal后需要調用remove()方法進行內存的清除。

接下來我們再延伸一下,想再來談談網絡上關于ThreadLocalMap中存儲大量Entry對象導致的內存“泄露”問題?

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

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

ThreadLocal應用場景

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

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

相關文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

最新評論