使用与CompletableFuture的默认公共fork/join池进行长时间阻塞调用是不好的做法吗?

Chi*_*may 11 java asynchronous java-8 completable-future

假设我有一个CompletableFuture,它包含一个阻塞调用,比如使用JDBC查询后端.在这种情况下,由于我没有将任何执行程序服务作为参数传递给CompletableFuture.supplyAsync(),因此通过后端获取资源的实际阻塞工作应该由公共Fork/Join池中的线程完成.让来自普通FJpool的线程阻塞调用不是不好的做法吗?我在这里的优点是我的主线程没有阻塞,因为我委托阻塞调用异步运行.在这里检查abt JDBC调用是否阻塞.如果这个推论是正确的,为什么可以选择使用带有CompletableFuture的默认公共FJpool?

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

fetchUnicorns.thenAccept(/**Do something with the result*/);
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 8

您不应该使用阻塞调用(以这种方式)的原因是,公共池并行性已配置为使用现有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;
    });
Run Code Online (Sandbox Code Playgroud)

ForkJoinPool.ManagedBlocker 是一个潜在阻塞操作的抽象,它允许Fork/Join池在识别出工作线程即将被阻塞时创建补偿线程.

很明显,它更容易使用

CompletableFuture<List<String>> fetchUnicorns  = 
    CompletableFuture.supplyAsync(() -> unicornService.getUnicorns(),
                                  Executors.newSingleThreadExecutor());
Run Code Online (Sandbox Code Playgroud)

这里.在生产环境中,您将保留对执行程序的引用,重用它并最终调用shutDown它.对于不重用执行程序的用例,

CompletableFuture<List<String>> fetchUnicorns  = 
    CompletableFuture.supplyAsync(() -> unicornService.getUnicorns(),
                                  r -> new Thread(r).start());
Run Code Online (Sandbox Code Playgroud)

就足够了,因为这样,线程将在作业完成后自动处理.


the*_*472 1

如果这个推论是正确的,为什么可以选择将默认的通用 FJpool 与 CompletableFuture 一起使用?

因为并非所有工作都是阻塞的。

您可以选择在自定义执行器上安排阻塞工作CompletableFuture.supplyAsync(Supplier<U>, Executor)