Active Object 主动对象模式
想象一个银行的 ATM 机。你把银行卡塞进去,按下取款按钮,ATM 立刻吐出凭证——但钱其实还在后台金库里清点。几秒钟后,ATM 显示「交易完成」或「余额不足」。这个「立刻返回、后台处理」的机制,就是 Active Object 模式的核心思想。
为什么 GUI 线程不能阻塞
在 GUI 应用中,有一个铁律:UI 线程(也称 Event Dispatch Thread)不能阻塞。如果用户在点击按钮后要等 5 秒才有响应,界面会完全卡死,用户体验极差。
// 错误示例:UI 线程被阻塞
button.addActionListener(e -> {
String result = networkCall(); // 可能阻塞 5 秒
label.setText(result); // UI 卡死
});
传统解法是开一个后台线程,但回调地狱很快就会失控:
// 回调地狱
new Thread(() -> {
String result = networkCall();
SwingUtilities.invokeLater(() -> label.setText(result));
}).start();
Active Object 模式提供了更优雅的解决方案:把「同步调用」变成「异步调用」,调用者立刻返回一个 Future,真实结果通过 Future 异步获取。
Active Object 模式结构
Active Object 模式由五个核心组件构成:
graph TB
subgraph Client[调用者]
C[客户端代码]
end
subgraph Proxy[Proxy 代理]
P[方法调度器<br/>Scheduler]
end
subgraph ActivationQueue[Activation Queue<br/>激活队列]
Q[MethodRequest 队列]
end
subgraph Servant[Servant 服务对象]
S[实际业务逻辑]
end
C --> P[1. 调用方法]
P --> Q[2. 入队 MethodRequest]
Q --> S[3. 取出并执行]
S --> P[4. 完成,Future 就绪]
P --> C[5. 返回 Future]
Java 实现示例
// 1. 定义方法请求接口
public interface MethodRequest {
void execute();
}
// 2. 定义 Servant(实际服务对象)
public class OrderService {
public CompletableFuture<Order> createOrder(OrderRequest request) {
// 真实的业务逻辑
return CompletableFuture.supplyAsync(() -> {
// 模拟耗时操作
Order order = orderRepository.save(request);
inventoryService.deduct(request.getItems());
return order;
});
}
}
// 3. 定义 Proxy
public class OrderServiceProxy {
private final ExecutorService executor;
private final OrderService servant;
public OrderServiceProxy(OrderService servant) {
this.executor = Executors.newCachedThreadPool();
this.servant = servant;
}
// 返回 CompletableFuture 作为 Future
public CompletableFuture<Order> createOrder(OrderRequest request) {
// 可以在此处做参数验证、日志记录
return servant.createOrder(request); // 异步执行
}
}
与 Future/Promise 模式的关系
Active Object 和 Future/Promise 模式解决的问题类似:如何处理异步操作。但它们的侧重点不同:
实际上,CompletableFuture 本身可以看作是 Active Object 模式的一个简化实现。它提供了异步调用的能力,但没有强调 Proxy、Scheduler 这些组件。
Swing Event Dispatch Thread:Java 中的 Active Object
Java GUI 编程中的 Event Dispatch Thread (EDT) 就是 Active Object 模式的经典应用。
// Swing 中正确的异步处理
public class SwingExample {
private JButton button;
private JLabel resultLabel;
public void onButtonClick() {
// 1. 在后台线程执行耗时操作
CompletableFuture.supplyAsync(() -> {
return networkService.fetchData(); // 不阻塞 EDT
}).thenAccept(result -> {
// 2. 结果更新回 EDT
SwingUtilities.invokeLater(() -> {
resultLabel.setText(result);
});
});
}
}
SwingUtilities.invokeLater() 的本质是把 UI 更新操作放入 EDT 的队列,由 EDT 在合适的时候执行。这就是典型的「方法请求入队、后台线程执行」的模式。
Netty ChannelHandler 的异步化
Netty 的 ChannelHandler 处理流程也体现了 Active Object 的思想:
public class BusinessHandler extends ChannelInboundHandlerAdapter {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
// 这里可以异步处理,不需要等待处理完成就返回
CompletableFuture
.supplyAsync(() -> processBusiness((Request) msg), businessPool)
.thenAccept(response -> {
// 写回响应
ctx.writeAndFlush(response);
})
.exceptionally(ex -> {
logger.error("处理失败", ex);
ctx.fireExceptionCaught(ex);
return null;
});
}
private Response processBusiness(Request request) {
// 实际业务逻辑
return businessService.handle(request);
}
}
Netty 的 Pipeline 中,每个 Handler 都可以异步化:收到消息后立即释放 ctx,让 Pipeline 可以继续接收下一个消息,处理结果通过回调写回。
方法请求的调度策略
Active Object 的 Scheduler 可以实现不同的调度策略:
先来先服务(FIFO):
public class FIFOScheduler implements Scheduler {
private final BlockingQueue<MethodRequest> queue =
new LinkedBlockingQueue<>();
@Override
public void enqueue(MethodRequest request) {
queue.put(request);
}
@Override
public void dispatch() {
MethodRequest request = queue.take();
request.execute();
}
}
优先级调度:
public class PriorityScheduler implements Scheduler {
private final PriorityBlockingQueue<MethodRequest> queue =
new PriorityBlockingQueue<>();
@Override
public void enqueue(MethodRequest request) {
queue.put(request); // MethodRequest 需要实现 Comparable
}
}
单线程 vs 多线程执行:
- 单线程 Scheduler:所有方法请求由同一个线程执行,保证顺序
- 多线程 Scheduler:使用线程池并行执行,提高吞吐
总结与延伸
Active Object 模式的核心价值是解耦方法的调用与执行:
优点:
- 调用线程不阻塞,立即返回
- 方法执行顺序可控(通过 Scheduler)
- 便于实现限流、熔断
缺点:
- 增加了复杂度(Proxy、Scheduler 等组件)
- 调试困难(调用栈不连续)
- 结果获取需要异步处理
适用场景:
- GUI 应用,避免 UI 线程阻塞
- 服务器端,高并发请求需要异步处理
- 需要控制执行顺序的场景
在现代 Java 中,CompletableFuture 已经提供了足够强大的异步编程能力,大多数场景不需要手动实现完整的 Active Object。但理解这个模式有助于理解 Netty、Actor Model 等框架的设计哲学。
那么问题来了:如果 Active Object 的 Servant 在执行过程中抛出异常,Future 会怎样?调用者如何感知并处理这些异常?这涉及到 CompletableFuture 的异常传播机制。