假设我有一个完整的未来,它包装了一个阻塞调用,比如使用JDBC查询后端。在这种情况下,由于我没有将任何执行器服务作为参数传递给CompletableFuture.supplyAsync(),因此通过后端获取资源的实际阻塞工作应该由公共Fork/Join池中的线程完成。让来自公共FJ池的线程进行阻塞调用不是不好的做法吗?我在这里的优势是我的主线程没有阻塞,因为我将阻塞调用委托给异步运行。在这里检查abt JDBC调用是否阻塞。如果这个推论是正确的,为什么要选择使用默认的公共FJ池和完整的未来?
CompletableFuture<List<String>> fetchUnicorns =
CompletableFuture.supplyAsync(() -> {
return unicornService.getUnicorns();
});
fetchUnicorns.thenAccept(/**Do something with the result*/);
您不应该使用阻塞调用(以这种方式)的原因是,假定非阻塞作业,公共池并行性已经配置为利用现有的CPU核心。阻塞的线程会降低使用同一个池的其他任务的并行性。
但是有一个官方的解决方案:
class BlockingGetUnicorns implements ForkJoinPool.ManagedBlocker {
List<String> unicorns;
public boolean block() {
unicorns = unicornService.getUnicorns();
return true;
}
public boolean isReleasable() { return false; }
}
CompletableFuture<List<String>> fetchUnicorns =
CompletableFuture.supplyAsync(() -> {
BlockingGetUnicorns getThem = new BlockingGetUnicorns();
try {
ForkJoinPool.managedBlock(getThem);
} catch (InterruptedException ex) {
throw new AssertionError();
}
return getThem.unicorns;
});
ForkJoinPool.ManagedBlocker
是潜在阻塞操作的抽象,它允许 Fork/Join 池在识别到工作线程即将被阻塞时创建补偿线程。
很明显,它更容易使用
CompletableFuture<List<String>> fetchUnicorns =
CompletableFuture.supplyAsync(() -> unicornService.getUnicorns(),
Executors.newSingleThreadExecutor());
在这里。在正式生产环境中,您将保留对执行器的引用,重用它,并最终对其调用关闭
。对于执行器未被重用的用例,
CompletableFuture<List<String>> fetchUnicorns =
CompletableFuture.supplyAsync(() -> unicornService.getUnicorns(),
r -> new Thread(r).start());
就足够了,因为这样,线程将在作业完成后自动处置。
如果这个推论是正确的,为什么可以选择使用默认的通用FJpool和CompletableFuture?
因为并非所有工作都是阻塞的。
您可以选择使用CompletableFuture.supplyAsync(供应商