CompletionHandler 是 Java NIO.2 中处理异步 I/O 回调的核心接口,含 completed(成功时调用,含结果与附件)和 failed(失败时调用,含异常与附件)两个方法,需注意附件生命周期、buffer 安全复用及回调线程模型。

CompletionHandler 是 Java NIO.2 中用于处理异步 I/O 操作结果的核心接口,它通过回调机制避免阻塞线程,适合高并发、低延迟场景。关键在于正确实现它的两个方法,并确保回调执行环境可控(如使用自定义线程池)。
理解 CompletionHandler 的两个核心方法
该接口定义了两个泛型方法:
- completed(V result, A attachment):操作成功时被调用,result 是操作返回值(如读取的字节数、通道对象),attachment 是发起操作时传入的上下文对象(常用于关联请求/响应或传递状态)
- failed(Throwable exc, A attachment):操作失败(超时、中断、I/O 错误等)时被调用,exc 是异常原因,attachment 与 completed 中一致,便于统一清理资源或记录日志
发起异步操作并绑定回调
以 AsynchronousSocketChannel 为例,发起异步读写需显式传入 CompletionHandler 实例和 attachment:
- 读操作:
channel.read(buffer, attachment, handler) - 写操作:
channel.write(buffer, attachment, handler) - 连接操作:
channel.connect(remote, attachment, handler) - attachment 可为 null,但推荐传入一个包含 buffer、session ID、callback 等信息的对象,避免闭包或静态变量共享状态
注意回调执行的线程模型
默认情况下,CompletionHandler 回调由 AsynchronousChannelGroup 内部线程(通常是 daemon 线程)触发,不保证与发起线程相同:
- 不要在回调中执行耗时操作(如数据库写入、复杂计算),否则会阻塞异步线程池
- 如需切换执行上下文,可在 completed/failed 中提交任务到业务线程池(如 ForkJoinPool.commonPool() 或自定义 Executor)
- 若需保证顺序性(如按请求顺序处理响应),需自行维护队列或使用 CompletableFuture 组合多个异步步骤
典型错误与规避方式
常见问题包括 attachment 生命周期错乱、buffer 复用冲突、异常未处理导致静默失败:
- buffer 在回调中可能仍被内核使用,不要在 completed 中立即重用或释放;建议每次读写使用独立 buffer,或在回调中调用
buffer.clear()前确认操作已完成 - attachment 若持有可变状态(如计数器、临时 buffer),需保证线程安全;推荐使用不可变对象或加锁
- failed 方法必须显式处理异常,至少记录日志;忽略它会导致连接断开、超时等问题难以排查










