深入理解 ThreadLocal:從原理到最佳實踐

📝 前言:為什么你需要了解 ThreadLocal?

????????在多線程并發編程中,線程安全始終是一個繞不開的話題。我們常常需要為每個線程維護一份獨立的上下文數據,例如用戶信息、事務 ID、日志追蹤 ID 等。這些數據不能被多個線程共享,否則會導致數據錯亂或線程安全問題。

????????Java 提供了一個非常優雅的工具類 —— ThreadLocal,它允許我們為每個線程綁定一個線程私有的變量副本,從而實現線程隔離、避免共享帶來的并發問題。

????????但其底層實現 ThreadLocalMap 的機制卻并不簡單,涉及到弱引用、哈希沖突處理、內存泄漏、清理機制、擴容策略等多個核心知識點。

先講結論,后解釋,因為我自己看javaguide的時候觀感就是這里一坨那里一坨的,對結論不是很清晰,導致讀者自己有一些理解,看javaguide的時候又有一些理解,對結論的記憶就不是很清晰):

但其底層實現 ThreadLocalMap 的機制卻并不簡單,涉及到弱引用、哈希沖突處理、內存泄漏、清理機制、擴容策略等多個核心知識點。

為了幫助你快速掌握重點,我先總結 ThreadLocal 的核心結論如下:

ThreadLocal 的核心結論

先說 ThreadLocal 的核心結論總結,供你快速掌握重點:

🧠 1. ThreadLocalMapThreadLocal 的靜態內部類

  • ThreadLocalMapThreadLocal靜態內部類

  • 包私有,無法通過外部直接訪問,只能通過 ThreadLocal.get()set() 等方法間接訪問。

  • 每個線程擁有自己的 ThreadLocalMap,所有 ThreadLocal 實例在該線程中都會映射到這個唯一的 Map


🔑 2. ThreadLocalMap 中的 Key 是弱引用

  • ThreadLocalMap 中的鍵值對結構為 Entry extends WeakReference<ThreadLocal<?>>

  • Key 是 ThreadLocal 實例的弱引用,Value 是強引用。

  • 這意味著:當外部沒有強引用指向某個 ThreadLocal 實例時,該實例可能被 GC 回收,此時 Entry.key 會被置為 null

  • 不是 ThreadLocal 本身是弱引用,而是 ThreadLocalMap.Entry 的 key 是弱引用。


🧮 3. ThreadLocalMap 的 Hash 算法

  • 索引計算方式index = key.threadLocalHashCode & (len - 1),與 HashMap 類似。

  • 不同點在于:

    • ThreadLocalthreadLocalHashCode 是全局唯一的,由原子遞增計數器生成。

    • 初始值為 0,每次遞增 0x61c88647(斐波那契數,有助于哈希分布更均勻)。

    • HashMap 的哈希值依賴于鍵對象的 hashCode() 方法。


🧱 4. 數據結構與沖突處理

  • ThreadLocalMap 使用數組結構,不使用鏈表。

  • Hash 沖突解決方式:采用線性探測法(開放尋址法)

  • 如果計算的索引位置被占用,則向后查找空槽插入。


🧹 5. Null Key 的清理機制

  • 探測式清理(expungeStaleEntry)

    • 從某個失效 Entry 出發,向后清理所有連續的 null Key Entry

    • 同時重新哈希有效的 Entry。

  • 啟發式清理(cleanSomeSlots)

    • set() 操作后觸發,隨機清理 log2(N) 個槽位

    • 防止內存泄漏擴散。

  • Set 操作時也會順帶清理一部分 Entry,清理范圍有限,從當前位置向后清理。類似于“貼羊肉包子的時候順便清理鍋邊的渣渣”。

  • Get 操作中如果遇到 Key 為 null 的 Entry,也會觸發探測式清理

  • 擴容時(rehash() / resize())會進行全局清理。(類似于“如果要從一個做羊肉包的小窯的時候換到大窯的時候,可以有空一次性清理全部 key 為 null 的 entry”。)


🔁 6. 擴容機制

方法觸發條件清理范圍行為說明
rehash()size >= 2/3 * len全局清理清理 null Key Entry(將無效的清理)
resize()rehash 后仍 size >= 0.75 * len全局清理 + 擴容擴容為 2 倍,并將有效 Entry 重新哈希放入新表(將有效的拿出來)

📦 7. set() 和 get() 的原理

  • set()

    • 計算索引 → 線性探測 → 插入或覆蓋 → 清理 null Entry → 擴容判斷

  • get()

    • 計算索引 → 若 key 不匹配 → 線性探測查找 → 若發現 null key → 啟動探測式清理

ThreadLocalMap 的常見問題解析

問題1:Entry的key-ThreadLocal<?> k 為什么要設計成弱引用?

原因: 設計成弱引用的原因;

  1. 內存泄漏的風險

    • 當一個 ThreadLocal 實例不再被任何強引用指向時(例如,用戶代碼中已經沒有對該 ThreadLocal 的引用),理論上它應該被垃圾回收。

    • 但如果 ThreadLocalMap 的 key 是強引用,那么即使外部已經沒有對該 ThreadLocal 的引用,ThreadLocalMap 仍然持有它的強引用,導致它永遠無法被回收。

    • 這樣就會造成 ThreadLocal 實例和對應的 value 都無法被釋放,從而引發內存泄漏。

  2. 弱引用避免了這個問題

    • 如果 key 是弱引用,當外部沒有強引用指向某個 ThreadLocal 實例時,垃圾回收器會在下一次 GC 時回收該 ThreadLocal 實例。

    • 此時,ThreadLocalMap 中對應的 key 會變成 null,但 value 仍然存在。ThreadLocalMap的清理機制 會在某些時機(如插入新條目時)清理這些 key 為 null 的條目,從而釋放 value 的內存。(也就是說弱引用可以盡量處理這個內存泄漏的問題,但是不能完全解決,強引用是直接沒辦法。完全解決的辦法,當然是直接remove整個entry,弱引用是保底措施。

問題2:當發生 GC 后,ThreadLocalMap 中的 key 是否為 null

在使用 ThreadLocal 時,一個常見問題是:當發生 GC 后,ThreadLocalMap 中的 key 是否為 null。

以下是所有可能的情況分析:

1.無外部強引用;

2.有外部強引用;

3.線程池復用;

4.ThreadLocal被重新賦值(這個就是改變了引用,其實可以當做無外部引用);

5.線程銷毀。

? 場景 1:無外部強引用(常見內存泄漏場景)

描述:
ThreadLocal 實例未被任何強引用持有,如局部變量使用后未清理。

GC 后的狀態:

  • Key 為 null(被回收)
  • Value 仍存在(內存泄漏)

代碼示例:

ThreadLocal<String> local = new ThreadLocal<>();
local.set("value");
local = null;
System.gc();

解決方案:

  • 調用?remove()?顯式清理
  • 使用?try-finally?確保清理

? 場景 2:有外部強引用

描述:
ThreadLocal 被靜態變量或成員變量引用。

GC 后的狀態:

  • Key 不為 null(未被回收)
  • Value 正常保留(無泄漏)

代碼示例:

static ThreadLocal<String> local = new ThreadLocal<>();
local.set("value");
System.gc();

解決方案:

  • 無需處理,只要強引用存在,GC 不會回收

? 場景 3:線程池復用線程

描述:

  • 如果使用 線程池,線程會被復用,ThreadLocal 的 Value 可能殘留。

  • 如果 ThreadLocal 被回收,但線程未銷毀,ThreadLocalMap 會積累大量 Entry,其中 Key 為 null,Value 無法回收。

  • 類似酒店給你的房間,房間復用了,但是沒有打掃衛生。

GC 后的狀態:

  • Key 可能為 nullThreadLocal?被回收)
  • Value 仍存在(內存泄漏)
  • 長期運行可能導致 OOM

解決方案:

  • 任務結束時調用?remove()
  • 使用?ThreadPoolExecutor.afterExecute()?鉤子清理

? 場景 4:ThreadLocal?被替換(重新賦值)

描述:
ThreadLocal 被重新賦值為新實例,舊實例無強引用。

GC 后的狀態:

  • 舊 Key 為 null
  • 舊 Value 仍存在(內存泄漏)

代碼示例:

ThreadLocal<String> local = new ThreadLocal<>();
local.set("old value");
local = new ThreadLocal<>(); // 舊對象無強引用
System.gc();
// ThreadLocalMap 中的 Entry:
//   Key: null(舊的 ThreadLocal 被回收)
//   Value: "old value"(內存泄漏)

解決方案:

  • 先調用?remove()?再賦值

? 場景 5:線程銷毀

描述:

  • 線程銷毀 時,ThreadLocalMap 會被回收,所有 Entry(包括 Key 和 Value)都會被 GC 清理。

  • 但如果使用 線程池,線程不會銷毀,ThreadLocal 的內存泄漏問題仍然存在。

GC 后的狀態:

  • ThreadLocalMap?被回收
  • 所有 Entry 被清理
  • Key 與 Value 都被釋放

解決方案:

  • 無需手動清理
  • 但線程池場景不適用此機制
? 最佳實踐
  1. 用完 ThreadLocal 必須調用 remove(),避免內存泄漏

    • 因為?Entry?的 key 是弱引用,value 是強引用
    • GC 后 key 會為 null,但 value 仍存在,造成內存泄漏
    • 推薦使用?try-finally?確保清理
  2. 避免在線程池中殘留 ThreadLocal,使用 try-finallyafterExecute 清理

    • 線程池中線程是復用的,不會自動銷毀?ThreadLocalMap
    • 若不清除,Entry 會持續累積,可能導致 OOM
    • 可使用?ThreadPoolExecutor.afterExecute()?統一清理
  3. 盡量使用 static final ThreadLocal,或至少 final ThreadLocal,避免被意外回收或替換。(場景1或者場景4)

    • final?表示引用不可變,防止被置 null 或重新賦值
    • 避免因弱引用導致 Key 丟失,即使你知道 value 是什么,也無法訪問
    • static?更適合全局上下文,如用戶信息、事務 ID 等
    • 如果只是對象內部狀態,非 static 的?final ThreadLocal?也足夠

問題3:ThreadLocalMap的set方法:

🔍 一、整體流程概覽
private void set(ThreadLocal<?> key, Object value) {Entry[] tab = table;int len = tab.length;// 1.計算哈希槽int i = key.threadLocalHashCode & (len - 1); // 2.線性探測查找插入的位置for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();// 3.處理keyif (k == key) { // 3.1如果 key 已存在,直接覆蓋 valuee.value = value;return;}if (k == null) { // 3.2如果 key=null(ThreadLocal 被 GC),替換舊 EntryreplaceStaleEntry(key, value, i);return;}}// 4.如果沒有沖突,直接存入新 Entrytab[i] = new Entry(key, value);int sz = ++size;// 5.啟發式清理 + 擴容判斷if (!cleanSomeSlots(i, sz) && sz >= threshold) {rehash(); // 重新哈希并且擴容}
}

🧱 二、詳細流程說明
? 1.?計算哈希槽
int i = key.threadLocalHashCode & (len - 1);
  • key.threadLocalHashCode?是一個全局遞增的哈希值。
  • 初始值為?0,每次遞增?0x61c88647(一個斐波那契數的十六進制值,用于均勻分布哈希值)。
  • len?是?Entry[] table?的長度(2 的冪)。
  • 使用位運算?& (len - 1)?模擬取模,提升效率。

? 作用:確定插入位置,減少哈希沖突。


? 2.?線性探測(開放尋址法)
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {// ...
}
  • 如果當前槽位不為空(發生哈希沖突),則向后查找空槽(nextIndex())。
  • nextIndex(i, len):向后移動一位,如果越界則從數組頭部開始(循環數組)。
  • 這種方式稱為?開放尋址法,與?HashMap?的鏈表法不同。

? 作用:解決哈希沖突,尋找合適插入位置。


? 3.?處理 Key 沖突或 Null Key

在循環中會遇到以下幾種情況:

🟢 情況 1:Key 相同(直接覆蓋)

if (k == key) {e.value = value;return;
}
  • 如果當前 Entry 的 key 與插入 key 相同,直接更新 value。

🟡 情況 2:Key 為 null(ThreadLocal 被 GC)

if (k == null) {replaceStaleEntry(key, value, i);return;
}
  • 表示該 Entry 的 key 已被 GC 回收。
  • 調用?replaceStaleEntry()?替換并清理該 Entry。
  • 該方法內部會調用?expungeStaleEntry(),進行?探測式清理,清除連續的 null key Entry。

? 作用:避免內存泄漏,及時清理無效 Entry。


? 4.?插入新 Entry
tab[i] = new Entry(key, value);
int sz = ++size;
  • 如果找到空槽位,直接插入新的 Entry。
  • 更新?size(Entry 數量)。

? 5.?啟發式清理 + 擴容判斷
if (!cleanSomeSlots(i, sz) && sz >= threshold) {rehash();
}
private void rehash() {expungeStaleEntries();// Use lower threshold for doubling to avoid hysteresisif (size >= threshold - threshold / 4)resize();}

🟢?cleanSomeSlots(i, sz)啟發式清理

  • 從當前索引開始,隨機清理 log?(size) 個槽位。
  • 如果清理了至少一個 null key Entry,返回?true

🟡?rehash()全局清理 + 擴容判斷

  • 調用?expungeStaleEntries()?清理所有 null key Entry。
  • 如果清理后仍超過擴容閾值(threshold = len * 2/3),則調用?resize()?擴容。
  • 擴容為原來的 2 倍,并重新哈希所有的有效 Entry。

? 作用:控制內存使用,避免 OOM,提升性能。

問題4: ThreadLocalMap的清理機制

1. .set()方法清理機制的思維導圖

2. 不同場景下的 set() 行為
(1) 最佳情況:槽位空閑(無沖突,無失效Entry)
  • 操作:直接插入新 Entry。

  • 附加清理:觸發 啟發式清理(cleanSomeSlots),以對數復雜度(log2(N))的步長掃描部分槽位,清理可能的失效 Entry。

  • 目的:預防性清理,減少未來內存泄漏風險。

(2) 哈希沖突:槽位被有效Entry占用(Key不匹配)
  • 操作:線性探測下一個槽位(i = nextIndex(i, len))。

  • 附加清理:無立即清理,但后續插入可能觸發清理。

(3) 發現失效Entry(Key=null)
  • 操作:觸發 替換式清理(replaceStaleEntry),分為以下步驟:

    1. 向前掃描: 從當前槽位向前遍歷,找到 最早的失效 Entry 位置(slotToExpunge)

      for (i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) {if (e.get() == null) slotToExpunge = i;
      }
    2. 向后掃描: 從當前槽位向后遍歷,處理兩種情況:

      • 找到相同 Key:替換值并調整位置。

      • 其他失效 Entry:擴展清理范圍。

    3. 探測式清理(expungeStaleEntry): 從 slotToExpunge 開始,向后清理連續失效 Entry,并重新哈希有效 Entry。

      private int expungeStaleEntry(int staleSlot) {// 清理當前槽位tab[staleSlot].value = null;tab[staleSlot] = null;size--;
      ?// 向后遍歷清理for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {ThreadLocal<?> k = e.get();if (k == null) {// 清理失效 Entry} else {// 重新哈希有效 Entry}}return i; // 返回第一個 null 的位置
      }


3. 關鍵清理方法對比
方法觸發條件清理范圍時間復雜度
啟發式清理cleanSomeSlots()插入新 Entry 后觸發隨機掃描 log2(N) 個槽位O(log N)
替換式清理replaceStaleEntry()遇到失效 Entry 時觸發向前找到鏈頭 + 向后連續清理O(n)
探測式清理expungeStaleEntry()replaceStaleEntry() 內調用清理連續失效 EntryO(n)

4. 設計思想總結
  1. 樂觀插入:優先保證插入效率,僅在必要時觸發清理。

  2. 惰性清理:不完全依賴 set() 清理,需手動 remove() 確保安全。

  3. 局部整理:通過重新哈希有效 Entry,減少后續操作沖突概率。

  4. 內存安全:弱引用 Key 防止內存泄漏,但需配合清理機制。

5.清理方式總結
清理方式觸發條件調用的清理方法清理范圍是否完全清理
set() 探測式清理遇到 key=nullEntryreplaceStaleEntry() 替換式清理 + expungeStaleEntry()探測式,比下面了多了一段向前掃描局部(探測路徑)遇到一個空槽就停止!?
get() 惰性清理遇到 key=nullEntryexpungeStaleEntry()局部(探測路徑)?
rehash() 全局清理size >= thresholdexpungeStaleEntries(),清理全部無效的。全局(遍歷所有位置)+局部探測??(接近完全);我覺得是完全?
resize() 完全清理rehash() 后仍需擴容只保留有效到新的容器。全局(遍歷全部位置+重建新表)?

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

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

相關文章

AD一張原理圖分成多張原理圖

先選中你要作為主級原理圖的那張原理圖&#xff0c;我這里是Sheet1,點擊設計&#xff08;D&#xff09;&#xff0c;再點擊create sheet symbol from sheet&#xff08;這個不能選錯&#xff09;快捷鍵&#xff1a;DY彈窗中選擇要加入的次級原理圖左擊鼠標放置重復上面的動作&a…

AI大模型資源

網址 https://sass.kupepoem.cn/ 登錄-選擇模型 打開上述網址后如下操作&#xff1a; 進入后&#xff0c;所有模型均可使用&#xff1a; o4-mini&#xff08;支持Deep Research深入研究&#xff09; 選擇o4-mini模型 選擇深度研究 要研究什么&#xff0c;對話直接輸入即可。…

論文閱讀--《Besting the Black-Box: Barrier Zones for Adversarial Example Defense》

好的&#xff0c;這是《Besting the Black-Box: Barrier Zones for Adversarial Example Defense》論文的中文翻譯&#xff1a;[文件名稱]: Besting_the_Black-Box_Barrier_Zones_for_Adversarial_Example_Defense.pdf [文件內容開始]第 1 頁 收稿日期&#xff1a;2021年10月1…

如何保證GPFS文件系統的強一致性

一、底層機制&#xff1a;分布式鎖與元數據管理GPFS 通過分布式鎖和集中式元數據管理的結合&#xff0c;確保數據和元數據的一致性&#xff1a;集中式元數據服務器&#xff08;MDS&#xff09;GPFS 采用主從架構的元數據管理&#xff0c;由指定節點&#xff08;或集群&#xff…

使用Docker+Nginx部署電商平臺項目(服務端+管理端+商城)

1.項目背景&#xff1a; 本項目作為小商場系統&#xff0c;涵蓋內容如下&#xff1a; litemall Spring Boot后端 Vue管理員前端 微信小程序用戶前端 Vue用戶移動端 1.1項目架構&#xff1a; 1.2項目技術棧&#xff1a; 本項目可以使用window運行jar包本地部署&#xff0c…

Java網絡編程入門:從基礎原理到實踐(二)

目錄 1. 網絡編程基礎&#xff1a;搞懂設備通信的底層邏輯 1.1 為啥需要網絡編程&#xff1f;—— 讓設備 “互通有無” 1.2 什么是網絡編程&#xff1f;—— 給數據 “定規矩、找路線” 1.3 網絡編程的基本概念&#xff1a;理清通信里的角色和流程 1.3.1 發送端和接收端 …

XSS內容分享

反射型XSS &#xff1a;反射型XSS 是非持久性、參數型的跨站腳本。反射型XSS 的JS 代碼在Web 應用的參數&#xff08;變量&#xff09;中&#xff0c;如搜索框的反射型XSS。在搜索框中&#xff0c;提交PoC[scriptalert(/xss/)/script]&#xff0c;點擊搜索&#xff0c;即可觸發…

電線桿距離居民區的安全距離【重要!!!】

10kV架空電線安全距離購房指南 中國大陸地區10kV架空電線距居民住宅需要滿足1.5米水平安全距離&#xff08;裸導線&#xff09;和6.5米垂直安全距離的國家強制標準。根據現行法規&#xff0c;10kV系統的電磁輻射水平極低&#xff0c;對居民健康影響可忽略不計&#xff0c;但購房…

河南萌新聯賽2025第(二)場:河南農業大學

我看到花兒在綻放 我聽到鳥兒在歌唱 我看到人們匆匆忙忙 我看到云朵在天上 我聽到小河在流淌 我看到人們漫步在路上 河南萌新聯賽2025第&#xff08;二&#xff09;場&#xff1a;河南農業大學 河南萌新聯賽2025第&#xff08;二&#xff09;場&#xff1a;河南農業大學_ACM/N…

unixbench系統性能測試

unixbench系統性能測試 環境&#xff1a; UnixBench: 6.0.0(2025-05-21)簡介 UnixBench 是一款經典的 Unix/Linux 系統性能測試工具&#xff0c;主要用于評估系統的CPU 運算能力、內存性能、多線程處理能力以及部分系統調用&#xff08;如進程創建、文件操作&#xff09;的效率…

上線了,自己開發的刷題小程序,vue3.0

嘿&#xff0c;最近我搞了個Java刷題的小程序&#xff0c;用Vue寫的&#xff0c;界面和功能都還挺完整的。今天就來跟大家聊聊這個小程序是怎么實現的&#xff0c;代碼里都藏著哪些小細節。 先看整體結構&#xff0c;我把整個頁面分成了幾個大塊&#xff1a;頂部導航欄、題目內…

嵌入式開發學習———Linux環境下數據結構學習(三)

單向循環鏈表單向循環鏈表是一種特殊的單向鏈表&#xff0c;尾節點的指針指向頭節點&#xff0c;形成一個閉環。適用于需要循環訪問的場景&#xff0c;如輪詢調度。結構特點&#xff1a;每個節點包含數據域和指向下一個節點的指針&#xff0c;尾節點的指針指向頭節點而非空值。…

【華為機試】684. 冗余連接

文章目錄684. 冗余連接描述示例 1示例 2提示解題思路核心分析問題轉化算法選擇策略1. 并查集 (Union-Find) - 推薦2. 深度優先搜索 (DFS)3. 拓撲排序算法實現詳解方法一&#xff1a;并查集 (Union-Find)方法二&#xff1a;深度優先搜索 (DFS)數學證明并查集算法正確性證明時間復…

Ⅹ—6.計算機二級綜合題7---10套

目錄 第7套 【填空題】 【修改題】 【設計題】 第8套 【填空題】 【修改題】 【設計題】 第9套 【填空題】 【修改題】 【設計題】 第10套 【填空題】 【修改題】 【設計題】 第7套 【填空題】 題目要求:給定程序中,函數fun的功能是:將形參s所指字符串中所…

【三橋君】大語言模型計算成本高,MoE如何有效降低成本?

? 你好&#xff0c;我是 ?三橋君? &#x1f4cc;本文介紹&#x1f4cc; >> 一、引言 在AI技術飛速發展的當下&#xff0c;大語言模型&#xff08;LLM&#xff09;的參數規模不斷增長&#xff0c;但隨之而來的計算成本問題也日益凸顯。如何在保持高效推理能力的同時擴…

Python游戲開發利器:Pygame從入門到實戰全解析

引言 Pygame是Python中最受歡迎的2D游戲開發庫之一&#xff0c;基于SDL&#xff08;Simple DirectMedia Layer&#xff09;構建&#xff0c;支持圖形渲染、音效處理、事件響應等核心功能。無論是開發簡單的休閑游戲&#xff0c;還是復雜的交互式應用&#xff0c;Pygame都能提供…

行為型模式-協作與交互機制

行為型模式聚焦于對象間的行為交互&#xff0c;通過規范對象協作方式提升系統的靈活性與可擴展性。在分布式系統中&#xff0c;由于多節點異步通信、網絡不可靠性及狀態一致性挑戰&#xff0c;行為型模式需針對分布式特性進行適應性設計。本文從觀察者、策略、命令、責任鏈、狀…

spring boot 整合 Spring Cloud、Kafka 和 MyBatis菜鳥教程

環境準備確保項目中已引入 Spring Boot、Spring Cloud、Kafka 和 MyBatis 的依賴。以下是一個典型的 Maven 依賴配置&#xff1a;<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artif…

20 BTLO 藍隊靶場 Sticky Situation 解題記錄

難度&#xff1a;5/10考察技能: Windows admin, Autopsy 使用場景&#xff1a;分析USB設備使用情況Autopsy使用注意&#xff1a;用管理員打開&#xff0c;在實際分析時注意先復制一個鏡像文件&#xff0c;保存好原文件常用的Windows USB 取證的位置:Windows XP:Registry Key: U…

安裝及配置Go語言開發環境與VSCode集成指南

安裝Go語言開發 安裝Go語言開發環境是第一步。訪問Go官網&#xff0c;下載適合操作系統的安裝包&#xff0c;如果進不去可以訪問Go官方鏡像站。 根據自己的系統選擇對應的安裝包&#xff0c;我這邊是Windows系統就點擊安裝第一個即可。 點擊下一步即可。 驗證安裝是否成功可以…