使用 future 和 completableFuture 中断读取方法

Interrupting read method with future and completableFuture

在解决任务时,我注意到一个我无法解释的行为。

我的任务是从 InputStream 读取并在超时后中断读取。尽管很多人说阻塞读取不能被中断,但我使用 CompletableFuture

实现了这个目标
public void startReader() {
   CompletableFuture<Void> future = CompletableFuture.runAsync(() -> doRead(System.in));
   future.get(5, TimeUnit.SECONDS);
}

private void doRead(InputStream in) {
   try {
      new BufferedReader(new InputStreamReader(in)).readLine();
   } catch (IOException e) {
      e.printStackTrace();
   }
}

但是当我使用 Future 实现相同的功能时,我可以看到 TimeoutException 被扔进了 JVM,但我仍然可以看到 读取线程没有终止并且仍然 运行

public void startReader() throws ExecutionException, InterruptedException, TimeoutException {
   Future<?> future = Executors.newSingleThreadExecutor().submit(() -> doRead(System.in));
   future.get(5, TimeUnit.SECONDS);
}

private void doRead(InputStream in) {
   try {
      new BufferedReader(new InputStreamReader(in)).readLine();
   } catch (IOException e) {
      e.printStackTrace();
   }
}

为什么会有这样的差异?我相信CompletableFuture不会变魔术

当您到达 future.get(5, TimeUnit.SECONDS); 时,您的两个代码片段都不会停止 "reading" 线程。他们将继续等待您来自 System.in 的输入。如果你想停止它,你应该向那个线程发送一个中断,并希望线程对此做出反应。显然,您也可以强制终止线程。

然而,CompletableFuture.runAsync()Executors.newSingleThreadExecutor() 调用使用不同的线程,特别是使用不同的 daemon flags (see What is a daemon thread in Java?)。当您在 doRead() 方法中放置一个 System.out.println(Thread.currentThread().isDaemon()); 时,您会看到 CompletableFuture.runAsync 使用守护线程(因此它不会阻止 JVM 终止),而 Executors.newSingleThreadExecutor() 不会(并使 JVM 保持活动状态)。