痛點:同步調用耦合業務與併發邏輯
直接在 Service 方法加 synchronized 存在四大問題:
- 單/多線程版本雙維護
- Client 同步阻塞,服務品質下降
- 無法實現優先級佇列、熔斷、限流等進階控制
- 業務邏輯與併發控制糾纏,難以復用測試
核心模式:Active Object(主動物件)
| 類型 | 被動物件 | 主動物件 |
| 方法調用 | 同步、阻塞 | 異步、非阻塞 |
| 執行執行緒 | 呼叫者執行緒 | 自身專屬執行緒 |
| 回傳值處理 | 直接回傳 | Future/Callback/Event |
| 適用場景 | 簡單、低併發 | 高併發、需排隊控制 |
框架三大核心組件
1. MethodRequest —— 命令模式封裝請求
public interface MethodRequest {
void call(); // 執行入口
}
// 具體命令:封裝 sayHello 請求
class SayHelloRequest implements MethodRequest {
private final Service service;
public SayHelloRequest(Service service) {
this.service = service;
}
@Override
public void call() {
service.sayHello(); // 在 Active Object 執行緒中執行
}
}
2. ActiveQueue —— 生產者消費者佇列
public class ActiveQueue {
private final Queue queue = new LinkedList<>();
private final int maxSize;
public ActiveQueue(int maxSize) {
this.maxSize = maxSize;
}
// 生產者:Client 提交請求
public synchronized void enqueue(MethodRequest request) throws InterruptedException {
while (queue.size() >= maxSize) {
wait(); // 佇列滿,等待消費
}
queue.add(request);
notifyAll(); // 喚醒消費者
}
// 消費者:Active Object 取走執行
public synchronized MethodRequest dequeue() throws InterruptedException {
while (queue.isEmpty()) {
wait(); // 佇列空,等待生產
}
MethodRequest request = queue.remove();
notifyAll(); // 喚醒生產者
return request;
}
}
3. ActiveObject —— 專屬執行緒循環消費
public class ActiveObject extends Thread {
private final ActiveQueue queue;
private volatile boolean running = true;
public ActiveObject(ActiveQueue queue) {
this.queue = queue;
setDaemon(true); // 不阻塞 JVM 關閉
setName("ActiveObject-Worker");
start();
}
public void enqueue(MethodRequest request) throws InterruptedException {
queue.enqueue(request);
}
@Override
public void run() {
while (running) {
try {
MethodRequest request = queue.dequeue();
request.call(); // 核心:在專屬執行緒執行業務邏輯
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break; // 優雅退出
} catch (Exception e) {
// 記錄錯誤,防止單一任務異常導致 Worker 死亡
System.err.println("任務執行異常: " + e.getMessage());
}
}
}
public void shutdown() {
running = false;
interrupt();
}
}
服務代理:對 Client 透明化異步
public class ServiceProxy implements Service {
private final Service realService = new ServiceImpl();
private final ActiveObject activeObject;
public ServiceProxy() {
this.activeObject = new ActiveObject(new ActiveQueue(100));
}
@Override
public void sayHello() {
try {
activeObject.enqueue(new SayHelloRequest(realService));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("提交任務被中斷", e);
}
}
}
Client 使用零感知
// 引入並發邏輯前
Service s = new ServiceImpl();
Client c = new Client(s);
c.requestService(); // 同步阻塞
// 引入並發邏輯後
Service s = new ServiceProxy(); // 僅此行變更
Client c = new Client(s);
c.requestService(); // 瞬間返回,異步執行
框架擴展點
| 需求 | 擴展方案 |
| 支援回傳值 | MethodRequest 回傳 Future,ActiveQueue 存 FutureTask |
| 優先級佇列 | ActiveQueue 改用 PriorityBlockingQueue,MethodRequest 實現 Comparable |
| 定時/週期任務 | 加入 ScheduledExecutorService,支援 scheduleAtFixedRate |
| 叢集高可用 | 引入分散式鎖、領導者選舉、狀態同步 |
| 監控指標 | 佇列長度、處理延遲、拒絕次數、執行緒池狀態 |
優缺點總結
| 優點 | 缺點 |
| 業務邏輯與併發模型完全解耦 | 增加類數量與間接層次 |
| Client 非阻塞,回應極快 | 大量 ActiveObject 導致執行緒切換開銷 |
| 靈活的佇列控制(優先級、限流、熔斷) | 異步除錯困難(堆疊追蹤斷裂) |
| ServiceImpl 可獨立單元測試 | 無回傳值場景受限(需 Future 擴展) |
| 符合單一職責、開閉原則 | 簡單場景過度設計 |
現代替代:CompletableFuture + 執行緒池
// 輕量級異步服務
@Service
public class AsyncService {
private final ExecutorService executor = Executors.newFixedThreadPool(10);
public CompletableFuture sayHelloAsync() {
return CompletableFuture.runAsync(() -> {
// 業務邏輯
System.out.println("Hello World!");
}, executor);
}
}
// Client
asyncService.sayHelloAsync()
.thenRun(() -> System.out.println("完成"))
.exceptionally(ex -> { log.error(ex); return null; });
總結
Active Object 框架展示了併發模式組件化的經典思路:命令封裝請求 + 佇列解耦生產消費 + 專屬執行緒隔離執行。雖現代開發多用 CompletableFuture、ExecutorService、Disruptor、Reactor 等成熟框架,但理解 Active Object 內部機制,有助於設計高內聚、低耦合的併發系統架構。