多線程同步是 Java 併發編程的核心課題。本文從「為何需要同步」切入,剖析共享變量與執行順序兩大核心問題,演示多種解決策略,手寫線程池演練管控邏輯,並全面講解 JDK java.util.concurrent (JUC) 包中的核心組件應用,助你構建健壯、高效的併發系統。
同步問題主要發生在「單對象、多線程」場景,核心癥結在於兩點:
多線程同時修改同一實例的實例變量(堆內存共享),導致數據競爭。例如兩線程同時累加 sum += i,預期各自得 5050,實際因交替覆蓋導致結果翻倍為 10100。
class MyRunner implements Runnable {
public int sum = 0; // 共享變量
public void run() {
for (int i = 1; i <= 100; i++) sum += i;
// ...
}
}
// 兩線程共用同一 runner 實例 → 數據競爭
多線程交替執行時,期望的原子操作序列(線程1完整執行步驟1-3,再輪到線程2)可能被打亂,導致業務邏輯錯誤。
每個線程使用獨立的 Runnable 實例,各自擁有獨立的 sum 變量,徹底消除共享。缺點:無法實現真正的數據共享協作,適用場景受限。
將 sum 移入 run() 方法內部,成為線程私有的棧幀局部變量。優勢:無鎖、高性能、線程絕對安全。核心思想:無共享,無傷害。
ThreadLocal —— 線程本地存儲ThreadLocal 為每個線程維護獨立的變量副本,邏輯上「全局可見」,物理上「線程隔離」。適用於線程內多方法傳遞上下文(如事務 ID、數據庫連接、用戶會話)。
public ThreadLocal<Integer> tl = new ThreadLocal<>();
public void run() {
tl.set(0); // 初始化
int sum = tl.get() + i;
tl.set(sum);
}
建議:優先考慮策略二(局部變量),其次策略三(ThreadLocal),避免策略一帶來的並發度限制。
synchronized 與鎖優化使用 synchronized(this) 將關鍵業務代碼包裹為臨界區,強制串行化執行。但需注意兩大原則:
// 反例:Sleep 在同步塊內,嚴重降低吞吐量
synchronized(list) {
for(...) list.add(...);
Thread.sleep(500); // 錯誤!
}
// 正例:僅同步核心操作
synchronized(list) {
for(...) list.add(...);
}
Thread.sleep(500); // 移至同步塊外
模擬網絡通訊場景,構建可啟動、休眠、喚醒、停止的線程池。核心組件:
MyThread:擁有狀態機(Initial/Running/Sleeping/Stopped),run() 中通過 wait()/notify() 實現休眠喚醒。MyThreadManager:統一管理線程陣列,提供 Start/Stop/Sleep/Wakeup 任務分發,內部使用 synchronized(thread) 保護狀態變更。MyThreadPool (Server端):ServerSocket 接受連接,每連接啟動一線程解析命令(Count/Status/StartAll/StopAll/SleepAll/WakeupAll),調用 Manager 執行。此實踐深度揭示了線程池的本質:線程複用 + 狀態機 + 命令模式 + 同步協作。
| 類型 | 特點 | 適用場景 |
|---|---|---|
FixedThreadPool | 固定核心數,隊列無界 | 長期任務、控制併發度 |
CachedThreadPool | 核心 0,最大無界,60s 回收空閒 | 短時任務、突發流量 |
ScheduledThreadPool | 支持定時/周期性執行 | 定時任務、延遲執行 |
Callable + Future / CompletionServiceCallable<T>:有返回值的任務單元Future<T>:異步結果持有者,get() 阻塞獲取ExecutorCompletionService:批量提交任務,take() 按完成順序獲取結果,極大簡化「多任務聚合」邏輯BlockingQueueLinkedBlockingQueue 內置鎖與條件變量,put() 滿阻塞,take() 空阻塞,天然實現線程安全的緩衝區協作,無需手寫 wait/notify。
Semaphore:資源訪問許可證構造時指定許可證數(如 2),acquire() 獲取,release() 釋放。實現「同時僅 N 線程訪問資源」的流控效果,適用於資源池、限流器。
CyclicBarrier vs CountDownLatch| 特性 | CyclicBarrier | CountDownLatch |
|---|---|---|
| 機制 | 攔截器:所有線程到達屏障點才共同繼續 | 倒計時:主線程等待計數歸零 |
| 可重用 | 是(reset()) | 否(一次性) |
| 典型場景 | 多線程分階段計算、迭代收斂 | 主線程等待多子線程初始化完成、並行計算聚合 |
| 核心方法 | await() | countDown() / await() |
CyclicBarrier 還支持可選的 barrierAction(屏障打開時執行的回調),適合分階段聚合計算。
ThreadLocal),從根源消除同步需求。Lock、BlockingQueue、Semaphore、CompletableFuture),降低 synchronized 直接使用頻率。Executors 默認工廠,自定義核心數、隊列類型、拒絕策略、線程命名、異常處理。CyclicBarrier,一次性等待用 CountDownLatch,資源許可用 Semaphore,數據流轉用 BlockingQueue。