runAfterBoth用于在两个CompletableFuture均完成(无论成功或异常)后同步执行无返回值的Runnable,适用于清理、日志等收尾操作,不消费结果、不支持链式调用、默认在后完成任务的线程执行。

runAfterBoth 适合在两个独立的异步任务都成功完成之后,执行一个无返回值的后续操作(比如资源释放、日志记录、状态清理等),它不关心前置任务的结果,只等待它们“完成”(包括正常结束或异常终止)。
runAfterBoth 的核心行为
它接收两个 CompletableFuture 和一个 Runnable,当且仅当两个前置 future 都进入终态(completed)后,才会触发 Runnable。注意:
- 即使其中一个 future 因异常而完成,只要另一个也完成了(无论成功或失败),runAfterBoth 的回调仍会执行;
- 它不传播异常,也不组合结果,纯粹是“时机同步 + 旁路动作”;
- 返回的是一个新的 CompletableFuture
典型清理场景示例
比如同时上传图片和元数据,不管哪边出错,只要两个请求都已响应(哪怕失败),就关闭临时文件流或清除本地缓存:
- 前置任务 A:上传图片到对象存储(返回 URL 或抛异常)
- 前置任务 B:写入数据库记录(返回主键 或 抛异常)
- runAfterBoth 回调:删除本地临时文件、释放 ByteBuffer、标记任务状态为“已尝试”
与类似方法的关键区别
避免误用,需明确对比:
- thenAcceptBoth:需要两个 future 都 正常完成,且要消费它们的结果;若任一 future 异常,回调不执行
- runAfterEither:只要任意一个完成就触发,不满足“双完成”约束
- whenComplete 或 handle:作用于单个 future,无法天然表达“等两个”的语义
- 若需“双成功才清理”,应改用 thenCombine + 手动判空/异常处理,而非 runAfterBoth
异常情况下的实际表现
runAfterBoth 不屏蔽异常,但自身回调仍会运行。例如:
- futureA.completeExceptionally(new IOException())
- futureB.complete("ok")
- runAfterBoth 的 Runnable 仍会执行 —— 这正是清理逻辑需要的:不管成败,该收尾就得收尾
- 但要注意:若清理逻辑自身抛异常,会作为新 CompletableFuture 的异常结果,影响下游 thenApply 等操作

















