【并發編程】ThreadLocal詳解與原理

📫作者簡介:小明Java問道之路2022年度博客之星全國TOP3,專注于后端、中間件、計算機底層、架構設計演進與穩定性建設優化,文章內容兼具廣度、深度、大廠技術方案,對待技術喜歡推理加驗證,就職于知名金融公司后端高級工程師。

? ? ? ? ? ?

🏆 2022博客之星TOP3 | CSDN博客專家 | 后端領域優質創作者 | CSDN內容合伙人

🏆 InfoQ(極客邦)簽約作者、阿里云專家 | 簽約博主、51CTO專家 | TOP紅人、華為云享專家

? ? ? ? ?

?🔥如果此文還不錯的話,還請👍關注、點贊、收藏三連支持👍一下博主~?


🍅 文末獲取聯系 🍅??👇🏻 精彩專欄推薦訂閱收藏 👇🏻

專欄系列(點擊解鎖)

學習路線(點擊解鎖)

知識定位

🔥Redis從入門到精通與實戰🔥

Redis從入門到精通與實戰

圍繞原理源碼講解Redis面試知識點與實戰

🔥MySQL從入門到精通🔥

MySQL從入門到精通

全面講解MySQL知識與企業級MySQL實戰

🔥計算機底層原理🔥

深入理解計算機系統CSAPP

以深入理解計算機系統為基石,構件計算機體系和計算機思維

Linux內核源碼解析

圍繞Linux內核講解計算機底層原理與并發

🔥數據結構與企業題庫精講🔥

數據結構與企業題庫精講

結合工作經驗深入淺出,適合各層次,筆試面試算法題精講

🔥互聯網架構分析與實戰🔥

企業系統架構分析實踐與落地

行業最前沿視角,專注于技術架構升級路線、架構實踐

互聯網企業防資損實踐

互聯網金融公司的防資損方法論、代碼與實踐

🔥Java全棧白寶書🔥

精通Java8與函數式編程

本專欄以實戰為基礎,逐步深入Java8以及未來的編程模式

深入理解JVM

詳細介紹內存區域、字節碼、方法底層,類加載和GC等知識

深入理解高并發編程

深入Liunx內核、匯編、C++全方位理解并發編程

Spring源碼分析

Spring核心七IOC/AOP等源碼分析

MyBatis源碼分析

MyBatis核心源碼分析

Java核心技術

只講Java核心技術

本文目錄

本文導讀

一、ThreadLocal是什么

二、ThreadLocal的數據結構

三、ThreadLocal源碼解析?

1、?ThreadLocal的set()方法

2、?ThreadLocal的get()方法

3、?ThreadLocal的remove()方法

四、ThreadLocal使用場景

五、ThreadLocal內存泄露原因

六、如何正確的使用ThreadLocal

七、ThreadLocal為什么不將key設置為強引用

總結


本文導讀

本文講解ThreadLocal是什么、ThreadLocal的數據結構以及ThreadLocal源碼set()/get()/remove()解析,ThreadLocal使用場景,如何正確的使用ThreadLocal,ThreadLocal內存泄露原因。

一、ThreadLocal是什么

ThreadLocal中填充的變量屬于當前線程,該變量對其他線程而言是隔離的,也就是說該變量是當前線程獨有的變量。

ThreadLocal為每一個線程都提供了變量的副本,使得每個線程在某一時間訪問到的并不是同一個對象,這樣就隔離了多個線程對數據的數據共享。

ThreadLocal 提供了線程本地的實例。ThreadLocal 變量通常被private static修飾。當一個線程結束時,它所使用的所有 ThreadLocal 相對的實例副本都可被回收。

ThreadLocal 適用于每個線程需要自己獨立的實例且該實例需要在多個方法中被使用,也即變量在線程間隔離而在方法或類間共享的場景。

二、ThreadLocal的數據結構

ThreadLocal是作為當前線程中屬性ThreadLocalMap集合中的某一個Entry的key值Entry(threadlocl,value),雖然不同的線程之間threadlocal這個key值是一樣,但是不同的線程所擁有的ThreadLocalMap是獨一無二的,也就是不同的線程間同一個ThreadLocal(key)對應存儲的值(value)不一樣,從而到達了線程間變量隔離的目的,但是在同一個線程中這個value變量地址是一樣的。?

1、每個Thread線程內部都有一個Map(ThreadLocalMap)

2、Map里面存儲ThreadLocal對象(key)和線程的變量副本(value)

3、Thread內部的Map是由ThreadLocal維護的,由ThreadLocal負責向map獲取和設置線程的變量值。

4、對于不同的線程,每次獲取副本值時,別的線程并不能獲取到當前線程的副本值,形成了副本的隔離互不干擾。

三、ThreadLocal源碼解析?

1、?ThreadLocal的set()方法

ThreadLocal? set賦值的時候首先會獲取當前線程thread,獲取thread線程中的ThreadLocalMap屬性。如果map屬性不為空,則直接更新value值,如果map為空,則實例化threadLocalMap,并將value值初始化。

public void set(T value) {Thread t = Thread.currentThread(); // 1、獲取當前線程// 2、獲取線程中的threadLocalMap ,如果threadLocalMap不為空直接更新要保存的變量值// 否則創建threadLocalMap 并賦值ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);  // 初始化thradLocalMap 并賦值
}

ThreadLocalMap 是 ThreadLocal 的內部靜態類,而它的構成主要是用Entry來保存數據 ,而且還是繼承的弱引用。在Entry內部使用ThreadLocal作為key,使用我們設置的value作為value。

static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> { // Java弱引用Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}
}

2、?ThreadLocal的get()方法

public T get() {Thread t = Thread.currentThread(); // 1、獲取當前線程ThreadLocalMap map = getMap(t);   // 2、獲取當前線程的ThreadLocalMapif (map != null) { // 3、如果map數據不為空,獲取threalLocalMap中存儲的值ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}// 如果是數據為null則初始化,TheralLocalMap中存放key為threadLocal值為nullreturn setInitialValue();
}

3、?ThreadLocal的remove()方法

remove方法直接將ThrealLocal對應的值從當前相差Thread中的ThreadLocalMap中刪除,?

public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);
}

四、ThreadLocal使用場景

ThreadLocal 適用于如下兩種場景:1、每個線程需要有自己單獨的實例;2、實例需要在多個方法中共享,但不希望被多線程共享。

1、存儲用戶Session(不同線程獲取到的用戶信息不一樣)

2、數據庫連接,處理數據庫事務

3、數據跨層傳遞

4、Spring使用ThreadLocal解決線程安全問題

五、ThreadLocal內存泄露原因

Entry將ThreadLocal作為Key,值作為value保存,它繼承自WeakReference,注意構造函數里的第一行代碼super(k),這意味著ThreadLocal對象是一個「弱引用」。

主要兩個原因:1、沒有手動刪除這個Entry;2、CurrentThread 當前線程依然運行

解決方案:1、只要在使用完下ThreadLocal,調用remove方法刪除對應的Entry,就能避免內存泄漏。

2、由于ThreadLocalMap 是 Thread 的一個屬性,被當前線程所引用,所以ThreadLocalMap的生命周期跟 Thread 一樣長。如果threadlocal變量被回收,那么當前線程的threadlocal 變量副本指向的就是key=null,也即entry(null,value),那這個entry對應的value永遠無法訪問到。如果ThreadLocal場景采用線程池,這樣就可能導致非常多的entry(null,value)出現,從而導致內存泄露。

綜上, ThreadLocal 內存泄漏的根源是:如果沒有手動刪除(remove()方法)對應 key 就會導致entry(null,value)的對象越來越多,從而導致內存泄漏。

六、如何正確的使用ThreadLocal

1、將ThreadLocal變量定義成private static的,這樣的話ThreadLocal的生命周期就更長,由于一直存在ThreadLocal的強引用,所以ThreadLocal也就不會被回收,也就能保證任何時候都能根據ThreadLocal的弱引用訪問到Entry的value值,然后remove它,防止內存泄露

2、每次使用完ThreadLocal,都調用它的remove()方法,清除數據。

七、ThreadLocal為什么不將key設置為強引用

如果key設計成強引用且沒有手動remove(),ThreadLocal ref被回收了,但是因為threadLocalMap的Entry強引用了threadLocal(key就是threadLocal), 造成ThreadLocal無法被回收。

當前線程始終有強引用鏈CurrentThread Ref → CurrentThread →Map(ThreadLocalMap)-> entry,Entry就不會被回收( Entry中包括了ThreadLocal實例和value),導致Entry內存泄漏,也就是說ThreadLocalMap中的key使用了強引用是無法完全避免內存泄漏的

弱引用比強引用可以多一層保障弱引用的 ThreadLocal 會被回收,對應value在下一次 ThreadLocaI 調用 get()/set()/remove() 中的任一方法的時候會被清除,從而避免內存泄漏。

總結

本文講解ThreadLocal是什么、ThreadLocal的數據結構以及ThreadLocal源碼set()/get()/remove()解析,ThreadLocal使用場景,如何正確的使用ThreadLocal,ThreadLocal內存泄露原因。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/160385.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160385.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160385.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【電路筆記】-電流源

電流源 文章目錄 電流源1、概述1.1 理想電流源1.2 實際電流源1.3 連接規則 2、依賴電流2.1 壓控電流源2.2 電流控制電流源 3、總結 本文為前面文章 電壓源的延續&#xff0c;我們將在本文介紹電流源。 與電壓源的情況類似&#xff0c;我們將首先介紹理想電流源的概念&#xff…

MySQL 8.2 Command Line Client打開時一閃而過閃退問題

MySQL8.2安裝成功后&#xff0c;發現打開MySQL 8.0 Command Line Client時出現一閃而過&#xff0c;打不開的情況。 解決方案&#xff1a; 1、打開MySQL 8.2 Command Line Client文件位置 2、右鍵選擇屬性 3、復制它的目標 4、我復制下來的目標路徑是這樣的&#xff0c;"…

關于 Docker

關于 Docker 1. 術語Docker Enginedockerd&#xff08;Docker daemon&#xff09;containerdOCI (Open Container Initiative)runcDocker shimCRI (Container Runtime Interface)CRI-O 2. 容器啟動過程在 Linux 中的實現daemon 的作用 Docker 是個劃時代的開源項目&#xff0c;…

[計算機網絡實驗]頭歌 實驗二 以太網幀、IP報文分析(含部分分析)

目錄 第1關&#xff1a;Wireshark基本使用入門 【實驗目的】 【實驗環境】 【本地主機、平臺虛擬機之間數據傳遞】 wireshark基本用法】 1、wireshark主界面 2、抓取分組操作 3、Wireshark窗口功能 4、篩選分組操作 【實驗操作】 ?編輯 第2關&#xff1a;Ethernet幀…

編程語言發展史:C++語言的發展和應用

預計更新 第一部分&#xff1a;早期編程語言 1.1布爾代數和機器語言 1.2匯編語言的出現和發展 1.3高級語言的興起 第二部分&#xff1a;主流編程語言 1.1 C語言的誕生及其影響 1.2 C語言的發展和應用 1.3 Java語言的出現和發展 1.4 Python語言的興起和特點 1.5 JavaScript語言…

基于Towers of Binary Fields的succinct arguments

1. 引言 Ulvetanna團隊Benjamin E. Diamond和Jim Posen 2023年論文《Succinct Arguments over Towers of Binary Fields》&#xff0c;開源代碼見&#xff1a; https://github.com/recmo/binius&#xff08;Rust Sage&#xff09;【基于plonky3等庫】 在該論文中&#xff1…

Apache POI簡介

三十二、Apache POI 32.1 介紹 Apache POI 是一個處理Miscrosoft Office各種文件格式的開源項目。簡單來說就是&#xff0c;我們可以使用POI在Java程序中對Miscrosoft Office各種文件進行讀寫操作。 一般情況下&#xff0c;POI都是用于操作Excel文件。 Apache POI 的應用場…

基于區域劃分的GaN HEMT 準物理大信號模型

GaN HEMT器件的大信號等效電路模型分為經驗基模型和物理基模型。經驗基模型具有較高精度但參數提取困難&#xff0c;特別在GaN HEMT器件工藝不穩定的情況下不易應用。相比之下&#xff0c;物理基模型從器件工作機理出發&#xff0c;參數提取相對方便&#xff0c;且更容易更新和…

火山引擎 ByteHouse 的增強型數據導入技術實踐

作為企業數字化建設的必備要素&#xff0c;易用的數據引擎能幫助企業提升數據使用效率&#xff0c;更好提升數據應用價值&#xff0c;夯實數字化建設基礎。 數據導入是衡量OLAP引擎性能及易用性的重要標準之一&#xff0c;高效的數據導入能力能夠加速數據實時處理和分析的效率。…

Sa-Token 整合Java17和SpringBoot

目錄 前言引入項目開啟登錄認證路由攔截鑒權解決兼容問題總結 前言 之前無意中發現Sa-Token權限認證框架&#xff0c;項目十分好用。 項目地址&#xff1a; https://github.com/dromara/sa-token 官網地址&#xff1a; https://sa-token.cc/doc.html#/start/example 我的個人…

如何輕松應對企業網絡管理挑戰,釋放網絡靈活性

企業在日常經營中&#xff0c;越來越依賴于云應用程序&#xff0c;分散的團隊和統一通信。這些變化使得保持網絡連接性不僅是必要的&#xff0c;而且對任務的成功完成至關重要。 傳統的廣域網&#xff08;WAN&#xff09;并不總能適應這些挑戰&#xff0c;因為它們往往無法提供…

不停的挖掘硬盤的最大潛能

從 NAS 上退休的硬盤被用在了監控的存儲上了。 隨著硬盤使用壽命的接近尾聲&#xff0c;感覺就是從高附加值數據到低附加值數據上。監控數據只會保留那么幾個月的時間&#xff0c;很多時候都會被覆蓋重新寫入。 有人問為什么監控數據不保留幾年的&#xff0c;那是因為監控數據…

java_函數式接口

文章目錄 一、什么是函數式接口二、四大核心函數式接口三、使用舉例 一、什么是函數式接口 如果一個接口只有一個抽象方法&#xff0c;那么該接口就是一個函數式接口函數式接口的實例可以通過 lambda 表達式、方法引用或者構造方法引用來創建如果我們在某個接口上聲明了 Funct…

【Unity入門】鼠標輸入和鍵盤輸入

Unity的Input類提供了許多監聽用戶輸入的方法&#xff0c;比如我們常見的鼠標&#xff0c;鍵盤&#xff0c;手柄等。我們可以用Input類的接口來獲取用戶的輸入信息 一、監聽鼠標輸入 GetMouseButtonUp 、GetMouseButtonDown、GetMouseButton input.GetMouseButtonDown和 inp…

從mysql源碼編譯出相應的庫和可執行文件及搭建mysql服務端

目錄 1. 問題的提出 2. 源碼下載 3. 升級或安裝某些前置軟件 3.1. 升級CMake 3.2. 升級gcc、g 4. 安裝依賴庫 4.1. 安裝OpenSSL 4.2. 安裝Curses 4.3. 安裝pkg-config 5. 編譯、安裝 6. 編譯結果、配置 7. 編譯錯誤處理 7.1. 錯誤1 7.2. 錯誤2 8. 搭建mysql數…

VMware三種網絡模式

橋接模式 NAT(網絡地址轉換模式) Host-Only(僅主機模式) 參考&#xff1a; vmware虛擬機三種網絡模式 - 知乎 (zhihu.com)

【中國平安社招校招】【內推】【當天內推】

中國平安社招校招內推 通過內推鏈接即時內推&#xff0c;反饋速度比正常要快(可私信問進度) 開放大量HC(不限崗位和地區,技術、設計、產品、運營、數據等都可內推) Step1&#xff1a;查看相關職位 瀏覽平安的招聘官網&#xff1a;復制以下地址至瀏覽器打開。注意以下鏈接包含…

一篇文章搞懂WPF動畫的使用技巧

WPF 動畫系統提供了豐富的功能&#xff0c;用于為 UI 元素創建流暢的動態效果。動畫可以應用于任何可用于渲染的屬性&#xff0c;比如位置、顏色、大小等。在 WPF 中&#xff0c;動畫是通過更改隨時間變化的屬性來實現的。 WPF動畫基本用法 例如實現如下的動畫效果&#xff1…

合并區間問題

以數組 intervals 表示若干個區間的集合&#xff0c;其中單個區間為 intervals[i] [starti, endi] 。請你合并所有重疊的區間&#xff0c;并返回 一個不重疊的區間數組&#xff0c;該數組需恰好覆蓋輸入中的所有區間 。 示例 1&#xff1a; 輸入&#xff1a;intervals [[1,…

Java如何獲取泛型類型

泛型&#xff08;Generic&#xff09; 泛型允許程序員在強類型程序設計語言中編寫代碼時使用一些以后才指定的類型&#xff0c;在實例化時作為參數指明這些類型。各種程序設計語言和其編譯器、運行環境對泛型的支持均不一樣。Ada、Delphi、Eiffel、Java、C#、F#、Swift 和 Vis…