ThreadLocal 提供**線程局部變量**:同一代碼路徑,不同線程訪問互不干擾的獨立副本。典型場景:
TransactionSynchronizationManager)RequestContextHolder)SimpleDateFormat、Random)public class SimpleThreadLocal<T> {
private final Map<Thread, T> map = new WeakHashMap<>();
public void set(T value) {
map.put(Thread.currentThread(), value);
}
public T get() {
return map.get(Thread.currentThread());
}
public void remove() {
map.remove(Thread.currentThread());
}
}
本質:Thread 內部持有 ThreadLocalMap(Key 為 ThreadLocal 弱引用,Value 為對象強引用)。set/get 實際操作當前線程的 Map。
ThreadLocalMap.Entry extends WeakReference<ThreadLocal>:Key 弱引用,防止 ThreadLocal 對象無法回收。remove(),Value 強引用導致對象殘留。最佳實踐:用完即刪,try-finally 保證清理。nextIndex = (i + 1) & (len - 1),擴容閾值 2/3。Thread.init() 複製 inheritableThreadLocals。public final class UserContextHolder {
private static final ThreadLocal<UserContext> TL = new ThreadLocal<>();
private UserContextHolder() {}
public static void set(UserContext ctx) {
TL.set(ctx);
}
public static UserContext get() {
return TL.get();
}
public static void clear() {
TL.remove();
}
// 業務代碼
public void processOrder(Order order) {
UserContext ctx = UserContextHolder.get();
// 直接使用 ctx.getUserId(),無需層層傳參
orderService.create(order, ctx.getUserId());
}
}
// Filter/Interceptor 中初始化與清理
public class UserContextFilter implements Filter {
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) {
try {
UserContext ctx = buildContext((HttpServletRequest) req);
UserContextHolder.set(ctx);
chain.doFilter(req, res);
} finally {
UserContextHolder.clear(); // 必須清理,防污染線程池
}
}
}
| 坑點 | 後果 | 對策 |
|---|---|---|
忘記 remove() | 線程池場景內存洩漏、數據串流 | try-finally / Filter/Interceptor 統一清理 |
| 靜態 ThreadLocal 存大對象 | 佔用 Heap,GC 困難 | 僅存輕量上下文,大對象用緩存 |
| 誤以為線程間共享 | 邏輯錯誤 | 明確語義:隔離而非共享 |
| 子線程繼承父線程值 | 非預期數據洩露 | 需繼承用 InheritableThreadLocal,否則默認隔離 |
ThreadLocal 以「空間換時間」實現線程隔離,是框架級組件傳遞上下文的利器。業務代碼應封裝為 XxxContextHolder 統一管理生命週期,**切記用完即清**,配合線程池使用時更需嚴守清理契約。