this。Class 對象。synchronized(obj)。// 死鎖:線程 1 鎖 A 再鎖 B,線程 2 鎖 B 再鎖 A
Object lockA = new Object(), lockB = new Object();
new Thread(() -> { synchronized(lockA){ sleep(100); synchronized(lockB){} } }).start();
new Thread(() -> { synchronized(lockB){ sleep(100); synchronized(lockA){} } }).start();
避免策略:固定加鎖順序、使用 tryLock(timeout) (ReentrantLock)、避免在鎖內調用外部方法。
Thread 內部持有 ThreadLocalMap (類似 HashMap,Key 為 ThreadLocal 對象弱引用,Value 為任意對象)。ThreadLocal.set(value):獲取當前線程 Map → 以 this 為 Key 存入 Value。ThreadLocal.get():獲取當前線程 Map → 以 this 為 Key 取值,無則調用 initialValue()。ThreadLocal.remove():從當前線程 Map 刪除 Key,防內存洩漏。TransactionSynchronizationManager 用 ThreadLocal 綁定 Connection,保證同一事務內多 DAO 共用同一連接。SimpleDateFormat、Random、JDBC Statement 等每線程一份,無需同步。public final class RequestContextHolder {
private static final ThreadLocal<RequestContext> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setContext(RequestContext ctx) {
CONTEXT_HOLDER.set(ctx);
}
public static RequestContext getContext() {
return CONTEXT_HOLDER.get();
}
public static void clear() {
CONTEXT_HOLDER.remove(); // 必須調用,防洩漏
}
}
// 攔截器中使用
public class ContextInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
RequestContext ctx = new RequestContext();
ctx.setUserId(getUserId(request));
ctx.setTraceId(UUID.randomUUID().toString());
RequestContextHolder.setContext(ctx);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
RequestContextHolder.clear(); // 關鍵:請求結束清理
}
}
原因:ThreadLocalMap Key 為弱引用,Value 強引用。線程池線程長期存活,若不 remove(),Key GC 後 Value 仍被 Entry 強引用 → 無法回收 → OOM。
防範:
try-finally 確保 remove() 執行。RequestContextListener、Netty FastThreadLocal)。父線程創建子線程時自動複製父線程的 InheritableThreadLocal 值。注意:線程池複用線程不會觸發複製,需配合 ThreadPoolTaskExecutor 的 ThreadFactory 或 RunnableWrapper 手動傳遞。
| 維度 | synchronized / Lock | ThreadLocal |
|---|---|---|
| 核心目標 | 互斥訪問共享資源 | 線程隔離,各線程獨享副本 |
| 性能開銷 | 上下文切換、CAS、內核轉換 | 極低 (Map 讀寫),無阻塞 |
| 適用場景 | 計數器、共享集合、複雜業務臨界區 | 連接綁定、上下文傳遞、非線程安全工具類隔離 |
| 內存風險 | 無特殊風險 | 線程池長生命周期導致 Value 洩漏 |
| 組合使用 | 保護 ThreadLocal Map 內部操作 (JDK 內部已處理) | 配合 synchronized 保護共享初始化邏輯 |
ConcurrentHashMap、LongAdder) 替代同步。set/remove,嵌套調用用 try-finally。TransmittableThreadLocal (阿里開源) 實現上下文傳遞。ThreadLocalMap 洩漏。