在高併發應用場景中,線程池是提升程式效能與併發能力的核心組件。本文將深入剖析如何利用隊列資料結構,從零構建一個簡潔高效的線程緩衝池,並詳細說明同步機制與阻塞等待的實現細節。
一個經典的線程緩衝池由兩大核心部分組成:
這種「生產者-消費者」模式能有效解耦任務提交與執行,實現異步處理。
採用固定大小的循環陣列實現隊列,具有 O(1) 時間複雜度的入隊與出隊操作:
oldest 指向最早入隊元素位置next 指向下一個可寫入位置next = (next + 1) % sizeoldest = (oldest + 1) % size關鍵狀態判斷:
oldest == next(next + 1) % size == oldestsize - 1面對多線程併發訪問,SyncQueue 類採用 synchronized 關鍵字保證原子性,並利用 wait()/notify() 實現阻塞等待:
public class SyncQueue {
private final Object[] _array;
private int _next = 0, _oldest = 0, _size;
public SyncQueue(int size) {
_array = new Object[size];
_size = size;
}
public synchronized void put(Object o) {
while (full()) {
try { wait(); }
catch (InterruptedException ex) { throw new RuntimeException(ex); }
}
_array[_next] = o;
_next = (_next + 1) % _size;
notify();
}
public synchronized Object get() {
while (empty()) {
try { wait(); }
catch (InterruptedException ex) { throw new RuntimeException(ex); }
}
Object ret = _array[_oldest];
_oldest = (_oldest + 1) % _size;
notify();
return ret;
}
private boolean empty() { return _next == _oldest; }
private boolean full() { return (_next + 1) % _size == _oldest; }
}
注意:while 迴圈而非 if 是防止虛假喚醒的關鍵,這是多線程編程中常見的陷阱。
任務採用 JDK 標準 Runnable 介面封裝,工作線程持續從隊列取任務執行:
public class Worker implements Runnable {
private final SyncQueue _queue;
public Worker(SyncQueue queue) { _queue = queue; }
public void run() {
while (true) {
Runnable task = (Runnable) _queue.get();
task.run();
}
}
}
// 建立容量為10的隊列,啟動5個工作線程
SyncQueue queue = new SyncQueue(10);
for (int i = 0; i < 5; i++) {
new Thread(new Worker(queue)).start();
}
// 提交任務
queue.put(new MyTask());
此實現為基礎框架,生產環境可擴展:異常處理機制、動態調整池大小、任務優先級、優雅關閉等高級功能。