ThreadLocal內部結構深度解析(Ⅰ)

目錄

使用ThreadLocal

?例子

內部結構分析

源碼解析

?圖示詳解


ThreadLocal是Java中一個非常重要且常用的線程局部變量工具類,它使得每個線程可以獨立地持有自己的變量副本,而不是共享變量,解決了多線程環境下變量共享的線程安全問題。下面我將從多個維度深入分析ThreadLocal的內部結構和工作原理。

使用ThreadLocal

// 1. 初始化:創建ThreadLocal變量
private static ThreadLocal<T> threadLocal = new ThreadLocal<>();// 2. 設置值:為當前線程設置值
threadLocal.set(value);  // value為要存儲的泛型對象// 3. 獲取值:獲取當前線程的值
T value = threadLocal.get();  // 返回當前線程存儲的值// 4. 移除值:清除當前線程的ThreadLocal變量(防止內存泄漏)
threadLocal.remove();

【注】使用時,通常將ThreadLocal聲明為static final以保證全局唯一性

private static ThreadLocal<T> threadLocal = ThreadLocal.withInitial(() -> initialValue);

【注:】 withInitial里面放的是任何能夠返回 T 類型實例的 Lambda / Supplier
只要 Supplier 的邏輯最終能 new(或從緩存、工廠、單例池等)拿出一個 T,就合法。

?例子

package com.qcby.test;import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class ThreadLocalTest {private List<String> messages = new ArrayList<>();public static final ThreadLocal<ThreadLocalTest> holder = ThreadLocal.withInitial(ThreadLocalTest::new);public static void add(String message) {holder.get().messages.add(message);}public static List<String> clear() {List<String> messages = holder.get().messages;holder.remove();return messages;}public static void main(String[] args) throws InterruptedException {// 創建線程池ExecutorService executor = Executors.newFixedThreadPool(10);// 提交10個任務for (int i = 0; i < 10; i++) {final int threadId = i;executor.submit(() -> {ThreadLocalTest.add("線程" + threadId + "的消息" );// 打印當前線程的消息System.out.println("線程" + threadId + "的消息列表: " + holder.get().messages);// 清除當前線程的ThreadLocalThreadLocalTest.clear();});}// 關閉線程池executor.shutdown();executor.awaitTermination(1, TimeUnit.SECONDS);// 主線程檢查自己的ThreadLocal(應該是空的)System.out.println("主線程的消息列表: " + holder.get().messages);}
}

內部結構分析

根據這里get的源碼追溯分析:

追溯到:

源碼解析
/*** 獲取當前線程的ThreadLocal變量值*/
public T get() {// 1. 獲取當前線程對象Thread t = Thread.currentThread();// 2. 獲取當前線程的ThreadLocalMap(線程私有數據存儲結構)ThreadLocalMap map = getMap(t);// 3. 如果map已存在if (map != null) {// 3.1 以當前ThreadLocal實例為key(也就是代碼中的holder),獲取對應的EntryThreadLocalMap.Entry e = map.getEntry(this);// 3.2 如果Entry存在if (e != null) {// 3.2.1 強轉為泛型類型并返回值@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}// 4. 如果map不存在或未找到值,初始化并返回默認值return setInitialValue();
}/*** 獲取線程的ThreadLocalMap(實際是Thread類的threadLocals字段)*/
ThreadLocalMap getMap(Thread t) {return t.threadLocals; // 直接返回線程對象的成員變量
}/*** 初始化值并存入ThreadLocalMap*/
private T setInitialValue() {// 1. 獲取初始值(子類可重寫initialValue()方法)T value = initialValue();// 2. 獲取當前線程Thread t = Thread.currentThread();// 3. 獲取線程的ThreadLocalMapThreadLocalMap map = getMap(t);// 4. 如果map已存在,直接設置值if (map != null) {map.set(this, value);} else {// 5. 如果map不存在,創建新map并存入初始值createMap(t, value);}// 6. 返回初始值return value;
}/*** 創建線程的ThreadLocalMap并存入第一個值*/
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}/*** 默認初始值實現(可被withInitial覆蓋)*/
protected T initialValue() {return null; // 默認返回null
}
?圖示詳解

所以執行結果:

可以看見一個線程中只有一個信息,而不是它們統一堆砌在一起,原因就是底層是每個線程創建了一個Map對象,每個Map的value就是存入的messages本質是對象,也就是T--ThreadLocalTest對象們,并且它們Map中的Entry中的Key值都是一樣的,都是這個ThreadLocal,也就是holder。

注】并不是每個線程的Map只能存放一個value對象,是這里我展示的例子里,一個線程只存了一條,完全可以存入很多條消息,然后add()時就會累加在Map已經創建好的Entry后面也就是:

當然既然是Map,存儲Entry就涉及Hash了,這個以后再詳談。

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

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

相關文章

Python 數據挖掘之數據探索

在數據挖掘的流程中&#xff0c;數據探索是非常關鍵的第一步&#xff0c;它能幫助我們深入了解數據的特點&#xff0c;為后續的預處理和模型構建打下堅實的基礎。我們主要圍繞四個方面展開&#xff1a;數據對象與特征、數據統計描述、數據可視化以及相關性和相似性度量。一、數…

高并發點贊場景Synchronized、AtomicLong、LongAdder 和 LongAccumulator性能分析

在高并發點贊場景中&#xff0c;我們需要一個高效、線程安全的計數器來記錄點贊數。synchronized、AtomicLong、LongAdder 和 LongAccumulator 都是 Java 中用于實現原子操作的類&#xff0c;但它們的性能在高并發下差異顯著。性能主要取決于線程競爭程度&#xff1a;競爭越高&…

postgreSQL的sql語句

目錄 一&#xff1a;前提準備1.postgreSQL的安裝可以參考我下面一片文章&#xff1a; 二&#xff1a;SQL語句 1.相同點&#xff1a;支持標準sql類型 2.參考詳細學習地址&#xff1a; 3.postgresql與mysql的不同點 一&#xff1a;前提準備 1.postgreSQL的安裝可以參考我下面…

vue3 JavaScript 數據累加 reduce

在Vue 3中&#xff0c;你可以使用JavaScript的reduce方法來處理數據累加。reduce方法通常用在數組上&#xff0c;它將數組中的每個元素通過一個累加器函數&#xff08;accumulator&#xff09;從左到右累積&#xff0c;最終生成一個單一的值。這在計算總和、累加值等場景中非常…

史上最清楚!讀者,寫者問題(操作系統os)

讀者-寫者問題是另一個里程碑式的同步互斥問題。它比生產者-消費者更復雜&#xff0c;因為它引入了不對稱的訪問權限&#xff1a;讀者和讀者之間是共享的&#xff0c;但寫者和任何人&#xff08;包括讀者和其他寫者&#xff09;之間都是互斥的。我們用一個生動的比喻來解析這個…

使用Starrocks替換Clickhouse的理由

背景 Starrocks和clickhouse都是非常優秀的OLAP數據庫&#xff0c;那么什么情況下使用clickhouse&#xff0c;什么場景下使用starrocks呢&#xff0c;本文就簡單列舉一下他們的優缺點 理由 首先兩者都是列存儲&#xff0c;并且都實現了列壓縮&#xff0c;所以從存儲中兩者的壓縮…

Mybatis 兩級緩存可能導致的問題

Mybatis 兩級緩存可能導致的問題兩級緩存簡介一級緩存 localCache效果開關二級緩存兩級緩存可能導致的問題分布式環境下查詢到過期數據事務隔離級別失效讀已提交失效讀未提交失效總結兩級緩存簡介 一級緩存 localCache 效果 一級緩存是 session 或者說事務級別的&#xff0c…

vue3+uniapp 使用vue-plugin-hiprint中實現打印效果

前言&#xff1a; vue3uniapp 使用vue-plugin-hiprint中實現打印效果 官網地址&#xff1a;gitee https://gitee.com/ccsimple/vue-plugin-hiprinthttps://gitee.com/ccsimple/vue-plugin-hiprint 實現效果&#xff1a; 預覽打印內容&#xff1a; 實現步驟&#xff1a; 1、安…

【elementUI踩坑記錄】解決 el-table 固定列 el-table__fixed 導致部分滾動條無法拖動的問題

目錄一、問題背景二、 問題現象三、核心原因四、解決辦法增強方案&#x1f680;寫在最后一、問題背景 在使用 Element UI 的 el-table 組件時&#xff0c;固定列功能雖然實用&#xff0c;但會引發滾動條交互問題&#xff1a; 固定列區域懸浮顯示滾動條但無法正常拖動滾動條 …

【機器人編程基礎】python文件的打開和關閉

文件的打開和關閉 在Python中,文件操作是一項基本而重要的任務,涉及到打開、讀取、寫入、關閉文件等操作。正確地管理文件對于數據持久化、輸入輸出處理等至關重要。下面將詳細解釋如何在Python中打開和關閉文件,并提供相應的代碼示例。 文件打開 在Python中,可以使用內…

ShenYu實戰、問題記錄

概述 一款高性能的國產的Apache開源API網關&#xff0c;官方文檔。 在ShenYu v2.6.1, ShenYu注冊中心只支持http類型&#xff0c;中間件注冊類型已經被移除。 所以&#xff0c;請使用http注冊類型來注冊你的服務。不是微服務注冊中心&#xff0c;它只是將元數據、選擇器數據、…

走近科學IT版:EasyTire設置了ip,但是一閃之后就變回到原來的dhcp獲得的地址

EasyTier 是一款簡單、安全、去中心化的內網穿透和異地組網工具&#xff0c;適合遠程辦公、異地訪問、游戲加速等多種場景。無需公網 IP&#xff0c;無需復雜配置&#xff0c;輕松實現不同地點設備間的安全互聯。 上次實踐的記錄&#xff1a;適合遠程辦公、異地訪問的EasyTier…

rk3588平臺USB 3.0 -OAK深度相機適配方法

目錄 文件更改記錄表 1、usb規則添加 2、拉取相關依賴 3、安裝python3、安裝pip 4、安裝依賴 5、安裝ffmeg 6、攝像頭功能測試 7、將視頻拷貝到U盤查看 1、usb規則添加 由于OAK是USB設備,因此為了在使用 udev 工具的系統上與之通信, 您需要添加udev規則以使…

工廠模式總結

工廠模式1. 簡單工廠模式&#xff08;Simple Factory&#xff09; 核心思想 定義一個工廠類&#xff0c;根據輸入參數創建不同的具體對象。客戶端不直接調用具體類的構造函數&#xff0c;而是通過工廠類獲取對象。 示例代碼 #include <iostream> #include <memory>…

MySQL的三種安裝方式(mis、zip、yum)

目錄 2.0數據庫安裝 2.1windows上.mis格式 環境準備 MySQL的安裝 環境配置&#xff08;非必要&#xff09; 2.2windows上.zip格式安裝 環境準備 配置文件的內容 MySQL的安裝 附錄可能出現問題 圖形工具遠程連接數據庫 2.3Linux上安裝yum包 環境準備 過程命令 My…

串口學習和藍牙通信HC05(第八天)

&#x1f468;?&#x1f4bb;個人主頁&#xff1a;開發者-削好皮的Pineapple! &#x1f468;?&#x1f4bb; hello 歡迎 點贊&#x1f44d; 收藏? 留言&#x1f4dd; 加關注?! &#x1f468;?&#x1f4bb; 本文由 削好皮的Pineapple! 原創 &#x1f468;?&#x1f4b…

設計總監的“輕量化”新武器:用Adobe Express,音頻一鍵驅動動畫

在快節奏的創意項目中&#xff0c;如何將復雜的設計理念或冗長的研究報告&#xff0c;快速轉化為易于理解、富有吸引力的動態內容&#xff0c;是衡量一個團隊溝通效率的關鍵。作為一名在海外設計界工作了十余年的設計師&#xff0c;我發現&#xff0c;最高效的團隊&#xff0c;…

零知開源——STM32F407VET6驅動SHT41溫濕度傳感器完整教程

?零知開源是一個真正屬于國人自己的開源軟硬件平臺&#xff0c;在開發效率上超越了Arduino平臺并且更加容易上手&#xff0c;大大降低了開發難度。零知開源在軟件方面提供了完整的學習教程和豐富示例代碼&#xff0c;讓不懂程序的工程師也能非常輕而易舉的搭建電路來創作產品&…

Linux流量分析:tcpdump wireshark

前言 最近因為工作需要&#xff0c;研究了下如何使用tcpdump和wireshark分析業務流量。如果要使用tcpdump分析具體的HTTP請求耗時&#xff0c;需捕獲網絡數據包并分析時間戳信息&#xff0c;重點關注TCP連接的建立、HTTP請求發送到響應接收的全過程。 以下是具體步驟和技巧&…

深度學習圖像分類數據集—角膜潰瘍識別分類

該數據集為圖像分類數據集&#xff0c;適用于ResNet、VGG等卷積神經網絡&#xff0c;SENet、CBAM等注意力機制相關算法&#xff0c;Vision Transformer等Transformer相關算法。 數據集信息介紹&#xff1a;角膜潰瘍識別分類&#xff1a;[dot, mix, slice] 訓練數據集總共有270張…