CompletableFuture.get 没有等待最后一个区块
CompletableFuture.get not waiting for final block
我有一个自定义的 ExecutorService,
class CustomExecutorService implements ExecutorService {
ExecutorService delegate;
public CustomExecutorService(ExecutorService delegate){
this.delegate = delegate;
}
// All methods are just delegated to the delegate except execute
public void execute(Runnable command) {
this.delegate.execute(wrapRunnable(command));
}
private Runnable wrapRunnable(final Runnable command) {
return () -> {
try {
command.run();
} finally {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finalizer done.");
}
};
}
}
现在这是我的测试代码,
ExecutorService executorService = new CustomExecutorService(Executors.newCachedThreadPool());
CompletableFuture.runAsync(() -> System.out.println("Command done."), executorService).get();
System.out.println("Main Thread Done.");
现在我希望发生以下输出,
Command done.
Finalizer done. //After a wait of 2 seconds
Main Thread Done.
但是发生的事情是,
Command done.
Main Thread Done.
它甚至没有等待 finally
块并且主线程正在退出。我无法解释这种行为,因为 CompletableFuture#get
应该是阻塞的,但它似乎不是在等待 finallly
阻塞。
我知道如果 finally
将由守护线程执行并且所有其他非守护线程在 finally
之前退出,则不能保证执行 finally
叫做。但是 ExecutorService 线程是非守护线程,所以 finally
应该被执行。
我是 运行 这个 corretto-11.0.11
查看CompletableFuture
的源代码第1627行(Jdk 1.8)如下所示:
public void run() {
CompletableFuture<Void> d; Runnable f;
if ((d = dep) != null && (f = fn) != null) {
dep = null; fn = null;
if (d.result == null) {
try {
f.run();
d.completeNull(); // line 1627
} catch (Throwable ex) {
d.completeThrowable(ex);
}
}
d.postComplete();
}
}
}
当 Runnable f
完成后,CompletableFuture 的结果将设置为 null
,然后 get()
方法将不再被阻塞。在你的情况下 Runnable f
等于 System.out.println("Command done.")
你可以在CompletableFuture runAsync(Runnable runnable, Executor executor)
的入口下个断点,一步步调试,你会发现什么时候设置了CompletableFuture的结果。
当您 运行 CompletableFuture.runAsync
时,它会将您的 Runnable
包装在一个任务中(来源中的 AsyncRun
),运行 是 运行nable,然后将 CompletableFuture 标记为完成。这是传递给执行者的内容。
在你的执行器中,你正在包装 那个 任务,所以当你的 finally
块 运行 时,未来已经标记为完成,这就是为什么get()
returns 紧随其后。
我有一个自定义的 ExecutorService,
class CustomExecutorService implements ExecutorService {
ExecutorService delegate;
public CustomExecutorService(ExecutorService delegate){
this.delegate = delegate;
}
// All methods are just delegated to the delegate except execute
public void execute(Runnable command) {
this.delegate.execute(wrapRunnable(command));
}
private Runnable wrapRunnable(final Runnable command) {
return () -> {
try {
command.run();
} finally {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finalizer done.");
}
};
}
}
现在这是我的测试代码,
ExecutorService executorService = new CustomExecutorService(Executors.newCachedThreadPool());
CompletableFuture.runAsync(() -> System.out.println("Command done."), executorService).get();
System.out.println("Main Thread Done.");
现在我希望发生以下输出,
Command done.
Finalizer done. //After a wait of 2 seconds
Main Thread Done.
但是发生的事情是,
Command done.
Main Thread Done.
它甚至没有等待 finally
块并且主线程正在退出。我无法解释这种行为,因为 CompletableFuture#get
应该是阻塞的,但它似乎不是在等待 finallly
阻塞。
我知道如果 finally
将由守护线程执行并且所有其他非守护线程在 finally
之前退出,则不能保证执行 finally
叫做。但是 ExecutorService 线程是非守护线程,所以 finally
应该被执行。
我是 运行 这个 corretto-11.0.11
查看CompletableFuture
的源代码第1627行(Jdk 1.8)如下所示:
public void run() {
CompletableFuture<Void> d; Runnable f;
if ((d = dep) != null && (f = fn) != null) {
dep = null; fn = null;
if (d.result == null) {
try {
f.run();
d.completeNull(); // line 1627
} catch (Throwable ex) {
d.completeThrowable(ex);
}
}
d.postComplete();
}
}
}
当 Runnable f
完成后,CompletableFuture 的结果将设置为 null
,然后 get()
方法将不再被阻塞。在你的情况下 Runnable f
等于 System.out.println("Command done.")
你可以在CompletableFuture runAsync(Runnable runnable, Executor executor)
的入口下个断点,一步步调试,你会发现什么时候设置了CompletableFuture的结果。
当您 运行 CompletableFuture.runAsync
时,它会将您的 Runnable
包装在一个任务中(来源中的 AsyncRun
),运行 是 运行nable,然后将 CompletableFuture 标记为完成。这是传递给执行者的内容。
在你的执行器中,你正在包装 那个 任务,所以当你的 finally
块 运行 时,未来已经标记为完成,这就是为什么get()
returns 紧随其后。