《編寫高質量代碼:改善 Java 程序的 151 個建議》第 8 章聚焦多線程與並發。本文精選建議 122~125,從線程異常自愈、volatile 同步誤區、異步計算接口選型、線程池核心原理四個維度,為高併發系統設計提供落地指導。
Socket 類長連接服務(如即時通訊、數據同步)常面臨兩大挑戰:避免線程阻塞、保證服務不中斷。一旦守候線程因不可預期異常退出,服務將不可用。
Thread.setUncaughtExceptionHandlerJava 1.5+ 提供線程級異常捕獲機制。示例模式:
class TcpServer implements Runnable {
public TcpServer() {
Thread t = new Thread(this);
t.setUncaughtExceptionHandler(new TcpServerExceptionHandler());
t.start();
}
public void run() { /* 業務邏輯 */ throw new RuntimeException(); }
static class TcpServerExceptionHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
// 1. 記錄異常堆棧,便於事後分析
// 2. 重啟新線程,保證服務不中斷
new TcpServer();
}
}
}
volatile 強制線程直接讀寫主內存,繞過工作內存(寄存器/Cache),保證可見性:線程 A 寫入後,線程 B 能立即讀到最新值。
volatile int count; count++ 仍不安全count++ 非原子操作(讀取 → 加一 → 寫入)。多線程併發時,即使讀到最新值,也會發生「丟失更新」:
線程 A 讀 998 → 線程 B 讀 998 → A 寫 999 → B 寫 999 → 結果 999(期望 1000)。
驗證代碼(啟動 1000 線程並發自增,循環檢測)可穩定復現線程不安全。
volatile 適用於單寫多讀、狀態標誌、雙重檢查鎖等場景;複合操作必須配合 synchronized、Lock 或 AtomicInteger 實現原子性。
run() 無返回值、不可拋檢查異常,獲取結果需自行設計共享變量或回調,擴展性差。
// 任務定義
class TaxCalculator implements Callable {
public Integer call() throws Exception {
TimeUnit.SECONDS.sleep(2); // 模擬耗時計算
return seedMoney / 10;
}
}
// 執行與進度監控
ExecutorService es = Executors.newSingleThreadExecutor();
Future future = es.submit(new TaxCalculator(100));
while (!future.isDone()) {
System.out.print("*"); // 進度提示
TimeUnit.MILLISECONDS.sleep(200);
}
System.out.println("稅金:" + future.get() + " 元");
es.shutdown();
Future 統一封裝「結果、異常、完成狀態」,異常可通過 future.get() 重拋捕獲;ExecutorService 實現線程生命週期管理。Java 線程狀態機:NEW → RUNNABLE → BLOCKED/WAITING → TERMINATED。TERMINATED 無法轉回 RUNNABLE,start() 二次調用拋 IllegalThreadStateException。
Runnable(無返回值)或 Callable(有返回值/異常);BlockingQueue,實現生產者-消費者解耦。ThreadPoolExecutor 為例)submit() → 包裝為 RunnableFuture → execute();execute():創建 Worker(不超過核心數)→ 任務入隊 → Worker 從隊列 take() 獲取任務;run() 循環:runTask() 執行用戶任務 → getTask() 阻塞取下一任務;await() 等待,新任務入隊 signal() 喚醒。Servlet 容器(Tomcat、Jetty)均採用線程池處理請求。若無池化,每請求創建銷毀線程將導致系統在高併發下 CPU 飆升、響應延遲、甚至 OOM。線程池將 T1(啟動) + T3(銷毀) 歸零,僅保留 T2(業務執行),是高併發系統的基礎設施。
並發編程無銀彈,但有原則:
✅ 關鍵服務加異常處理器實現自愈,但要警惕資源鎖、髒數據、內存洩漏;
✅ volatile 只負責可見性,複合操作上鎖;
✅ 異步有結果用 Callable,無結果用 Runnable,統一交給線程池調度;
✅ 線程池是生產環境標配,理解 Worker-Queue-Task 三角關係,合理配置核心數、隊列類型、拒絕策略。
掌握這四點,已解決 80% 以上的並發工程難題。