orTimeout 不会自动取消或中断底层任务执行,仅对 CompletableFuture 自身调用 completeExceptionally(new TimeoutException());原始任务仍可能继续运行并修改共享状态,需配合可中断线程池与主动中断检查才能实现真正熔断。

orTimeout 是 JDK 9 引入的声明式超时控制入口,它不阻塞、不轮询、不手动 cancel,但**默认不中断底层线程或任务执行**——这是绝大多数人踩坑的起点。
orTimeout 会自动取消正在运行的任务吗?
不会。orTimeout 只在超时发生时对 CompletableFuture 自身调用 completeExceptionally(new TimeoutException()),而原始任务(比如 supplyAsync 中的 lambda)仍可能继续跑完甚至修改共享状态。
- 如果你依赖「超时即终止」语义,必须额外封装可中断逻辑(例如检查
Thread.currentThread().isInterrupted()) -
orTimeout的 cancel 行为仅作用于其内部注册的延迟任务(由Delayer调度),不影响你传入的异步任务本身 - 验证方式:在
supplyAsync的 lambda 里加Thread.sleep(5000)并配orTimeout(1, TimeUnit.SECONDS),日志仍会打印完成,只是结果被丢弃
如何让 orTimeout 真正“熔断”下游任务?
关键不是改 orTimeout,而是改你提交的任务。需满足两个条件:
- 任务必须运行在支持中断的执行器上(如
new ThreadPoolExecutor(...),而非ForkJoinPool.commonPool()) - 任务体内部要主动响应中断:捕获
InterruptedException或定期检查Thread.interrupted()
示例:
ExecutorService executor = new ThreadPoolExecutor(
2, 2, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(),
r -> {
Thread t = new Thread(r);
t.setDaemon(false); // 非守护线程,确保能被 interrupt
return t;
}
);
CompletableFuture<String> future = CompletableFuture
.supplyAsync(() -> {
try {
Thread.sleep(3000); // 模拟长耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
throw new RuntimeException("task interrupted", e);
}
return "done";
}, executor)
.orTimeout(1, TimeUnit.SECONDS);
orTimeout 和 get(timeout, unit) 的行为差异
二者触发超时的时机和副作用完全不同:
-
get(1, SECONDS):调用线程阻塞等待,超时后抛TimeoutException,但原始任务仍在后台运行 -
orTimeout(1, SECONDS):立即返回原CompletableFuture,超时后自动写入异常结果,**不阻塞调用线程** - 重要区别:
orTimeout后续链式操作(如thenApply)会在超时发生时收到TimeoutException;而get超时是调用方自己处理,无法影响后续异步流
生产环境必须注意的线程池配置
orTimeout 内部使用单线程守护调度器 Delayer.delayer,它不参与你的业务线程池管理。但如果你的任务本身依赖线程池资源(比如数据库连接池、HTTP 客户端),超时后未释放资源会导致泄漏:
- 务必为耗时任务指定独立、有界、带拒绝策略的线程池,避免挤占公共池
- 不要复用
commonPool—— 它不可配置、无监控、中断行为不可控 - 超时后建议显式清理:在
exceptionally回调中关闭连接、释放锁、记录告警
真正难的不是加 orTimeout,而是让整个异步链路具备可中断性、可观测性和资源确定性。超时只是熔断的信号,不是终点。

















