《Thinking in Java》第 21 章通過三個豐富的仿真案例,展示了阻塞隊列、線程協作、動態調度等並發核心模式。本文保留原著設計精髓,代碼升級為 Java 8+ 風格,並添加關鍵點註解。
| 類 | 職責 | 關鍵並發機制 |
Customer | 不可變數據類,攜帶 serviceTime | 無狀態,線程安全 |
CustomerLine | 繼承 ArrayBlockingQueue<Customer> | 有界隊列,生產者/消費者自動阻塞 |
CustomerGenerator | 定時 put() 客戶 | 生產者線程 |
Teller | take() 客戶 → sleep(serviceTime) → 計數 | 消費者線程,synchronized 狀態切換 |
TellerManager | 定期掃描隊列/出納員比率,PriorityQueue 按服務數調度 | 調度器線程,wait/notify 控制出納員掛起/恢復 |
關鍵代碼片段:動態調度邏輯
public void adjustTellerNumber() {
int ratio = customerLine.size() / workingTellers.size();
if (ratio > 2) { // 排隊過長 → 增加出納員
if (!tellersDoingOtherThings.isEmpty()) {
Teller t = tellersDoingOtherThings.remove();
t.serveCustomerLine(); // notifyAll 喚醒
workingTellers.add(t);
} else {
Teller t = new Teller(customerLine);
exec.execute(t);
workingTellers.add(t);
}
} else if (workingTellers.size() > 1 && ratio < 2) { // 閒置過多 → 減少
reassignOneTeller(); // 移至做其他事隊列
}
}
運行效果
控制台實時打印隊列狀態與活躍出納員 ID,觀察隨負載自動擴縮容:
[Customer@1, Customer@2...] {T1 T2 T3}
[Customer@5...] {T1 T2}
[] {T1}
案例二:飯店點餐全流程協作
業務流程
- 顧客入座 → 侍者分配。
- 顧客點餐 → 侍者提交訂單到
orders 隊列。
- 廚師從
orders 取單 → 製作 → 成品放入對應侍者的 filledOrders 隊列。
- 侍者從
filledOrders 取盤 → 送達顧客 placeSetting(SynchronousQueue)。
- 顧客
take() 盤子 → 用餐 → 循環下一道菜。
隊列拓撲:零直接耦合
Customer ──(placeOrder)──▶ WaitPerson ──(orders.put)──▶ Restaurant.orders
Chef ──(orders.take)──▶ Restaurant.orders
Chef ──(filledOrders.put)──▶ WaitPerson.filledOrders
WaitPerson ──(placeSetting.put)──▶ Customer.placeSetting (SynchronousQueue)
Customer ──(placeSetting.take)──▶ 用餐
精妙點:所有線程間零直接調用,完全通過阻塞隊列傳遞消息對象,實現「共享內存」→「通信共享內存」範式轉換。
關鍵類結構
Order:攜帶 customer、waitPerson、food 信息的傳遞對象。
Plate:成品載體,關聯 Order 以便路由回正確侍者/顧客。
SynchronousQueue<Plate>:容量 0,put/take 必須配對,完美模擬「上菜-取菜」同步交接。
案例三:汽車裝配線——CyclicBarrier 同步裝配
流水線階段
ChassisBuilder:生產底盤 → chassisQueue。
Assembler:從 chassisQueue 取底盤 → 雇傭 3 類機器人並行裝配 → CyclicBarrier(4) 等待全部完成 → 放入 finishedQueue。
EngineRobot / DriveTrainRobot / WheelsRobot:實現 performService(),調用 Car.addXxx()。
RobotPool:管理機器人池,hire(Class<? extends Robot>, Assembler) 分配空閒機器人並 engage()。
Reporter:消費 finishedQueue 打印完整車輛信息。
CyclicBarrier 同步機制
// Assembler 中
private CyclicBarrier barrier = new CyclicBarrier(4); // 1 Assembler + 3 Robots
// 雇傭機器人
robotPool.hire(EngineRobot.class, this);
robotPool.hire(DriveTrainRobot.class, this);
robotPool.hire(WheelsRobot.class, this);
barrier.await(); // 4 方到齊才繼續,保證單車裝配原子性
finishedQueue.put(car);
RobotPool 分配邏輯
public synchronized void hire(Class<? extends Robot> type, Assembler a) throws Exception {
for (Robot r : pool) {
if (r.getClass().equals(type)) {
pool.remove(r);
r.assignAssembler(a).engage(); // 關聯組裝線 + 喚醒線程
return;
}
}
wait(); // 池空則等待歸還
hire(type, a); // 遞歸重試
}
設計亮點
- 資源池化:機器人復用,避免頻繁創建銷毀。
- 屏障同步:
CyclicBarrier 確保 3 道工序並行且全部完成才流轉下一輛車。
- 狀態機:
Robot 狀態:空閒 → 分配 → 執行 → 屏障等待 → 歸還池 → 掛起。
三大案例並發模式總結對比
| 模式 | 銀行出納員 | 飯店點餐 | 汽車裝配線 |
| 核心協作組件 | ArrayBlockingQueue + PriorityQueue | 多層 BlockingQueue (LinkedBlockingQueue, SynchronousQueue) | BlockingQueue + CyclicBarrier + 對象池 |
| 動態調度 | ✅ TellerManager 根據負載增減線程 | ❌ 固定廚師/侍者數 | ❌ 固定機器人池大小 |
| 同步原語 | wait/notify (Teller 掛起/恢復) | 隱式 (Queue 阻塞) | CyclicBarrier (多方同步) |
| 資源池化 | ❌ 出納員動態創建/銷毀 | ❌ | ✅ RobotPool 復用機器人 |
| 擴展性 | 易增減出納員邏輯 | 易增菜品/廚師類型 | 易增裝配工序/機器人類型 |
現代化改造建議(Java 21+)
- 使用 Virtual Threads (Project Loom) 替換平台線程,百萬級客戶/訂單無壓力。
- 用 Structured Concurrency (JEP 453) 管理線程組生命週期,自動傳播取消/異常。
- 用 Record 定義
Customer、Order、Plate 等不可變數據載體。
- 用 Sealed Interface 定義
Robot 類型層級,編譯期保證窮盡匹配。
- 上一篇文章: Spring MVC 生產環境數據源憑證隱藏方案:自定義 PropertyPlaceholderConfigurer 實現敏感配置外部化
- 下一篇文章: 【Java 設計模式】靜態代理模式深度解析:從原理到實戰
Copyright ©
程式碼之家 All Rights Reserved