ThreadLocal 實例通常作為 private static 字段出現在類中,用於關聯線程。它是線程級別的局部變量,關鍵點:
ThreadLocal 實例時,為每個線程提供獨立的變量副本,線程間互不影響。ThreadLocal<T> 僅有四個方法:
void set(T value):設置當前線程的變量副本。T get():獲取當前線程的變量副本。void remove():移除當前線程的變量副本,減少內存佔用(JDK 5.0 新增)。線程結束時局部變量自動被 GC 回收,顯式調用非必須但可加速回收。protected T initialValue():延遲加載方法,默認返回 null,通常通過匿名內部類重寫指定初始值。package com.test;
public class ThreadMain {
// ① 匿名內部類重寫 initialValue() 指定初始值
private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
// ② 獲取下一個序列值
public int getNextNum() {
seqNum.set(seqNum.get() + 1);
return seqNum.get();
}
public static void main(String[] args) {
ThreadMain sn = new ThreadMain();
// ③ 3個線程共享 sn,各自產生序列號
TestClient t1 = new TestClient(sn);
TestClient t2 = new TestClient(sn);
TestClient t3 = new TestClient(sn);
t1.start();
t2.start();
t3.start();
}
private static class TestClient extends Thread {
private ThreadMain sn;
public TestClient(ThreadMain sn) { this.sn = sn; }
@Override
public void run() {
for (int i = 0; i < 3; i++) {
// ④ 每個線程打印 3 個序列值
System.out.println("thread[" + Thread.currentThread().getName() + "] --> sn[" + sn.getNextNum() + "]");
}
}
}
}
輸出結果:
thread[Thread-0] --> sn[1]
thread[Thread-2] --> sn[1]
thread[Thread-1] --> sn[1]
thread[Thread-2] --> sn[2]
thread[Thread-0] --> sn[2]
thread[Thread-2] --> sn[3]
thread[Thread-1] --> sn[2]
thread[Thread-1] --> sn[3]
thread[Thread-0] --> sn[3]
每個線程擁有獨立的 seqNum 副本,從 1 開始遞增,互不干擾。
深度源碼分析暫不展開,參考:https://www.importnew.com/17849.html
get() 方法源碼片段:
ThreadLocal 通常聲明為 static。若不斷創建且未調用 remove(),會導致內存洩漏,尤其在高並發 Web 容器中。ThreadLocal 比 synchronized 更簡單、方便,並發性能更高(空間換時間,無鎖)。