在 Java 多線程編程中,ThreadLocal 是一個極具特色的工具類,它為每個線程提供獨立的變量副本,從根本上解決了線程安全問題。雖然概念上可以將其理解為一個 Map<Thread, T>,鍵為當前線程對象 Thread.currentThread(),值為線程專屬數據,但 ThreadLocal 實際上是對這一映射關系的封裝,確保線程只能訪問自己的數據,無法干擾其他線程的狀態。
ThreadLocal 提供了三個核心方法,構成了完整的生命週期管理:
nullpublic class ThreadLocalDemo {
private static final ThreadLocal<String> threadName = new ThreadLocal<>();
public static void main(String[] args) {
String[] names = {"A", "B", "C", "D", "E"};
for (int i = 0; i < names.length; i++) {
final String myName = names[i];
new Thread(() -> {
threadName.set(myName);
try {
Thread.sleep(500); // 模擬業務處理延遲
} catch (InterruptedException e) {
e.printStackTrace();
}
printThreadName();
}, "Thread-" + i).start();
}
}
private static void printThreadName() {
System.out.println(Thread.currentThread().getName() + " -> 我的名字叫" + threadName.get());
}
}
輸出結果清晰展示了線程隔離效果:
Thread-0 -> 我的名字叫A Thread-2 -> 我的名字叫C Thread-1 -> 我的名字叫B Thread-3 -> 我的名字叫D Thread-4 -> 我的名字叫E
每個線程通過 threadName.set() 設置的值,僅在自身線程上下文中可見,printThreadName() 方法無需傳參即可精準獲取當前線程的專屬數據,這正是 ThreadLocal「線程封閉」特性的直觀體現。
每個 Thread 實例內部持有一個 ThreadLocalMap 類型的 threadLocals 字段,這才是真正存儲數據的地方。ThreadLocal 對象本身不存儲數據,僅充當「鍵」的角色,通過 threadLocals.set(this, value) 將值存入當前線程的 Map 中。
ThreadLocalMap 的 Entry 繼承自 WeakReference<ThreadLocal>,鍵為弱引用。當 ThreadLocal 外部強引用消失時,GC 可回收鍵對象,但值對象若未及時 remove(),將因線程長期存活(如線程池)導致內存洩漏。最佳實踐:使用完畢後必須調用 remove() 清理。
TransactionSynchronizationManager 即基於 ThreadLocal 實現連接綁定SimpleDateFormat、Random 等當需要子線程自動繼承父線程的 ThreadLocal 值時,可使用 InheritableThreadLocal。其原理是在線程創建時,將父線程的 inheritableThreadLocals 複製給子線程,適用於線程池任務繼承上下文信息的場景。
set() → use() → remove() 模式,防止內存洩漏掌握 ThreadLocal 的核心機制與避坑指南,能讓你在高併發系統設計中遊刃有餘,編寫出更優雅、高效的線程安全代碼。