CompletableFuture.get(timeout)为何未终止任务?运行结果不符预期
问题:CompletableFuture超时后为何仍会执行完成?
我运行两个CompletableFuture实例,它们均会等待1秒后向控制台输出内容。我在0.5秒后对第一个实例调用带超时的get方法,预期仅第二个实例输出,但实际两者都输出了,这是怎么回事?
代码示例
CompletableFuture<Void> c1 = CompletableFuture.runAsync(() -> { System.out.println("Start CF 1"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(1); }); CompletableFuture<Void> c2 = CompletableFuture.runAsync(() -> { System.out.println("Start CF 2"); try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } System.out.println(2); }); long start = System.currentTimeMillis(); try { c1.get(500, TimeUnit.MILLISECONDS); } catch (TimeoutException e) { System.out.println("CF interrupted after " + (System.currentTimeMillis() - start) + "ms"); } c2.get();
实际输出
Start CF 1 Start CF 2 CF interrupted after 510ms 2 1
原因解析
调用get(long timeout, TimeUnit unit)触发超时,不会中断CompletableFuture对应的异步任务:
- CompletableFuture默认使用ForkJoinPool中的线程执行异步任务,
get超时只是当前线程放弃等待,不会向执行任务的线程发送中断信号 - 第一个任务里的
Thread.sleep(1000)没有被中断,会正常执行完毕,因此最后会输出1 - 后续调用
c2.get()会等待第二个任务完成并输出2,此时第一个任务仍在后台运行,等sleep结束后就会输出1
如何实现超时终止任务
如果要在超时后真正停止任务,需要手动处理中断逻辑:
- 在任务中主动检测线程中断状态,捕获InterruptedException后直接退出
- 保存任务执行线程的引用,超时后主动调用
interrupt() - 结合
orTimeout/completeOnTimeout方法让Future进入完成状态,同时在任务内处理中断
示例修改后的第一个任务:
CompletableFuture<Void> c1 = CompletableFuture.runAsync(() -> { System.out.println("Start CF 1"); try { // 分段sleep并检测中断 for (int i = 0; i < 10; i++) { if (Thread.currentThread().isInterrupted()) { return; } Thread.sleep(100); } } catch (InterruptedException e) { // 捕获中断后直接退出 return; } System.out.println(1); });
内容的提问来源于stack exchange,提问作者Johnny
相关产品推荐
相关产品推荐

