需返回结果用supplyAsync(),无返回值用runAsync();均应显式传入自定义线程池;thenApply()转换结果,thenAccept()消费结果,thenRun()无视结果执行;异常处理优先用whenComplete()或handle();join()抛原始异常,get()抛ExecutionException包装异常。

CompletableFuture.runAsync() 和 supplyAsync() 怎么选
看返回值需求:需要异步计算并返回结果,用 supplyAsync();只执行无返回值任务(比如发通知、写日志),用 runAsync()。
两者默认都使用 ForkJoinPool.commonPool(),但线程池资源有限,高并发下容易成为瓶颈。生产环境建议显式传入自定义线程池。
-
runAsync(() -> doSomething(), executor)—— 适合副作用操作 -
supplyAsync(() -> fetchData(), executor)—— 返回CompletableFuture,后续可链式处理 - 别直接用无参版本,
commonPool()不支持设置队列大小或拒绝策略,出问题难排查
thenApply()、thenAccept()、thenRun() 的行为差异
这三个方法都属于“消费上一阶段结果”的回调,但签名和用途严格不同:
-
thenApply():接收上一阶段的返回值,必须返回新值(类型可变),用于转换(如String→Integer) -
thenAccept():接收上一阶段返回值,但不返回任何东西(void),适合记录、校验等消费动作 -
thenRun():不接收任何参数,纯粹执行后续逻辑,相当于“不管前面啥结果,现在我要干点别的”
错误用法示例:future.thenAccept(x -> System.out.println(x)).thenRun(() -> sendEmail()) 看似合理,但如果 thenAccept 抛异常,thenRun 不会执行——它不感知上游异常,得用 whenComplete() 或 exceptionally() 补偿。
立即学习“Java免费学习笔记(深入)”;
异常处理不能只靠 exceptionally()
exceptionally() 只捕获上一阶段抛出的异常,且仅当该阶段是 supplyAsync 或 thenApply 这类“有返回值”的阶段才生效。如果异常发生在 runAsync 或 thenAccept 中,它压根收不到。
AJAX即“Asynchronous Javascript And XML”(异步JavaScript和XML),是指一种创建交互式网页应用的网页开发技术。它不是新的编程语言,而是一种使用现有标准的新方法,最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容,不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。《php中级教程之ajax技术》带你快速
更可靠的做法是组合使用:
- 用
whenComplete((result, ex) -> { ... })—— 无论成功失败都会进这个回调,ex非空即表示出错了 - 用
handle((result, ex) -> { ... })—— 类似whenComplete,但必须返回值,可用于兜底转换 - 避免在
exceptionally()里吞掉异常却不记录:它返回的是新CompletableFuture,原异常已“消失”,下游无法感知
CompletableFuture.supplyAsync(() -> {
if (Math.random() > 0.5) throw new RuntimeException("boom");
return "ok";
}).handle((result, ex) -> {
if (ex != null) {
log.error("Async task failed", ex);
return "fallback";
}
return result;
});
join() 和 get() 在阻塞等待时的区别
两者都用于同步获取结果,但异常包装方式不同:
-
get()抛ExecutionException(包装原始异常),还强制声明InterruptedException -
join()抛原始异常(如RuntimeException),不声明检查异常,更适合函数式链式调用后兜底
注意:join() 在任务被取消时抛 CancellationException,而 get() 会抛 CancellationException 包装在 ExecutionException 里——这点影响日志归因和监控告警匹配逻辑。
别在线程池任务里调用 join() 或 get(),容易导致线程饥饿;如真需同步,确保在非工作线程(如主线程)中调用。










