Java 使用带有CompletableFuture的默认公共fork/join池进行长阻塞调用是否是一种错误的做法?

Java 使用带有CompletableFuture的默认公共fork/join池进行长阻塞调用是否是一种错误的做法?,java,asynchronous,java-8,completable-future,Java,Asynchronous,Java 8,Completable Future,假设我有一个CompletableFuture,它封装了一个阻塞调用,就像使用JDBC查询后端一样。在这种情况下,由于我没有将任何executor服务作为参数传递给CompletableFuture.SupplySync(),因此通过后端获取资源的实际阻塞工作应该由公共分叉/联接池中的线程完成让来自普通FJpool的线程执行阻塞调用不是很好吗?我在这里的优势是我的主线程没有阻塞,因为我将阻塞调用委托给异步运行。检查abt JDBC调用是否被阻塞。如果这个推论是正确的,为什么我们可以选择使用默认的

假设我有一个CompletableFuture,它封装了一个阻塞调用,就像使用JDBC查询后端一样。在这种情况下,由于我没有将任何executor服务作为参数传递给CompletableFuture.SupplySync(),因此通过后端获取资源的实际阻塞工作应该由公共分叉/联接池中的线程完成让来自普通FJpool的线程执行阻塞调用不是很好吗?我在这里的优势是我的主线程没有阻塞,因为我将阻塞调用委托给异步运行。检查abt JDBC调用是否被阻塞。如果这个推论是正确的,为什么我们可以选择使用默认的通用FJpool和CompletableFuture

CompletableFuture<List<String>> fetchUnicorns  = 
    CompletableFuture.supplyAsync(() -> {
        return unicornService.getUnicorns();
    });

fetchUnicorns.thenAccept(/**Do something with the result*/);
CompletableFuture fetchUnicorns=
CompletableFuture.SupplySync(()->{
返回unicornService.getUnicorns();
});
fetchUnicorns.thenAccept(/**对结果做点什么*/);
如果这个推论是正确的,为什么我们可以选择使用默认的通用FJpool和CompletableFuture

CompletableFuture<List<String>> fetchUnicorns  = 
    CompletableFuture.supplyAsync(() -> {
        return unicornService.getUnicorns();
    });

fetchUnicorns.thenAccept(/**Do something with the result*/);
因为并不是所有的工作都是阻塞的


您可以选择在具有CompletableFuture.supplyAsync(供应商,执行者)

的自定义执行器上安排阻塞工作。您不应该使用阻塞调用(以这种方式)的原因是,公共池并行性已配置为利用现有的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;
    });
在这里。在生产环境中,您将保留对执行器的引用,重用它,并最终对其调用
shutDown
。对于不重用执行器的用例

CompletableFuture<List<String>> fetchUnicorns  = 
    CompletableFuture.supplyAsync(() -> unicornService.getUnicorns(),
                                  r -> new Thread(r).start());
CompletableFuture fetchUnicorns=
CompletableFuture.SupplySync(()->unicornService.getUnicorns(),
r->新线程(r.start());

这样就足够了,线程将在作业完成后自动释放。

CompletableFuture
作为API有许多设计缺陷,这就是其中之一。是的,在任何线程池(包括FJ池)中混合阻塞和非阻塞任务都是不好的做法。感谢您提供有关ForkJoinPool.ManagedBlocker的信息。有帮助!这个答案真的很有帮助!谢谢