Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/345.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 具有CompletableFutures且没有自定义执行器的代码是否只使用与内核数相等的线程数?_Java_Multithreading_Java 8_Future_Completable Future - Fatal编程技术网

Java 具有CompletableFutures且没有自定义执行器的代码是否只使用与内核数相等的线程数?

Java 具有CompletableFutures且没有自定义执行器的代码是否只使用与内核数相等的线程数?,java,multithreading,java-8,future,completable-future,Java,Multithreading,Java 8,Future,Completable Future,我正在阅读java 8 in action,第11章(关于CompletableFutures),它让我想到了我公司的代码库 《java 8 in action》一书说,如果你有下面我写的代码,你一次只能使用4个CompletableFuture(如果你有一台4核计算机)。这意味着,如果您想异步执行例如10个操作,您将首先运行前4个CompletableFutures,然后是第二个4,然后是剩下的2个,因为默认的ForkJoinPool.commonPool()只提供了等于Runtime.get

我正在阅读java 8 in action,第11章(关于
CompletableFuture
s),它让我想到了我公司的代码库

《java 8 in action》一书说,如果你有下面我写的代码,你一次只能使用4个CompletableFuture(如果你有一台4核计算机)。这意味着,如果您想异步执行例如10个操作,您将首先运行前4个
CompletableFuture
s,然后是第二个4,然后是剩下的2个,因为默认的
ForkJoinPool.commonPool()
只提供了等于
Runtime.getRuntime().availableProcessors()的线程数

在我公司的代码库中,有名为
asynchHelper
s的
@Service
类,其中包含一个方法
load()
,该方法使用
CompletableFuture
s在单独的块中异步加载有关产品的信息。我想知道他们是否一次只使用4个线程

在我公司的代码库中有几个这样的异步助手,例如有一个用于产品列表页(PLP)和一个用于产品详细信息页(PDP)。产品详细信息页面是专门针对特定产品的页面,显示其详细特征、交叉销售产品、类似产品以及更多内容

架构决定将pdp页面的详细信息分块加载。加载应该异步进行,当前代码使用
CompletableFuture
s。让我们看看伪代码:

static PdpDto load(String productId) {
    CompletableFuture<Details> photoFuture =
            CompletableFuture.supplyAsync(() -> loadPhotoDetails(productId));
    CompletableFuture<Details> characteristicsFuture =
            CompletableFuture.supplyAsync(() -> loadCharacteristics(productId));
    CompletableFuture<Details> variations =
            CompletableFuture.supplyAsync(() -> loadVariations(productId));

    // ... many more futures

    try {
        return new PdpDto( // construct Dto that will combine all Details objects into one
                photoFuture.get(),
                characteristicsFuture.get(),
                variations.get(),
                // .. many more future.get()s
        );
    } catch (ExecutionException|InterruptedException e) {
        return new PdpDto(); // something went wrong, return an empty DTO
    }
}
要加载的静态PDID(字符串productId){
完全未来=
CompletableFuture.supplyAsync(()->loadPhotoDetails(productId));
完整的未来特征未来=
CompletableFuture.SupplySync(()->loadCharacteristics(productId));
完全未来变化=
CompletableFuture.supplyAsync(()->loadVariations(productId));
//……还有更多的未来
试一试{
返回新的PDTO(//构造将所有细节对象合并为一个的Dto
photoFuture.get(),
characteristicsFuture.get(),
变量。get(),
//…更多的未来。获取()s
);
}捕获(ExecutionException | InterruptedException e){
return new pdto();//如果出现问题,请返回一个空的DTO
}
}
如您所见,上面的代码没有使用自定义执行器

这是否意味着,如果加载方法有10个
CompletableFuture
s,并且当前有2个人加载PDP页面,并且我们总共有20个
CompletableFuture
s要加载,那么所有这20个
CompletableFuture
s不会一次全部执行,而是一次只执行4个

我的同事告诉我,每个用户将获得4个线程,但我认为JavaDoc非常清楚地说明了这一点:

公共静态ForkJoinPool commonPool() 返回公共池实例。这个水池是静态建造的;其运行状态不受shutdown()或shutdownNow()尝试的影响。但是,此池和任何正在进行的处理将在program System.exit(int)时自动终止。任何依赖异步任务处理在程序终止之前完成的程序都应该在退出之前调用commonPool()


这意味着我们网站的所有用户只有1个池和4个线程。

是的,但比这更糟糕

公共池的默认大小比处理器/核心数少1个(如果只有1个处理器,则为1个),因此实际上一次处理3个,而不是4个

但是您最大的性能损失是并行流(如果您使用它们),因为它们也使用公共池。流用于超高速处理,因此您不希望它们与繁重的任务共享资源

如果您的任务设计为异步(即需要几毫秒以上),那么您应该创建一个池来运行它们。这样的池可以由所有调用线程静态创建和重用,这避免了每次使用时创建池的开销。您还应该通过对代码进行压力测试来调整池大小,以找到最佳大小,从而最大限度地提高吞吐量和减少响应时间

在我公司的代码库中,有[…]类[…]包含方法load(),该方法使用CompletableFutures加载信息[…]

那么,您是说
load()
方法等待I/O完成吗

如果是这样,并且@Bohemian所说的是真的,那么您不应该使用默认线程池

@Bohemian说,默认池的线程数与主机的CPU数大致相同。如果您的应用程序有很多与计算相关的任务要在后台执行,那就太好了。但是如果你的应用程序有很多线程在等待来自不同网络服务的回复,那就不太好了。那是一个完全不同的故事


我不是这方面的专家,我不知道如何(除了做实验)找出线程的最佳数量,但不管这个数量是多少,它与系统的CPU数量几乎没有关系,因此,您不应该为此使用默认池。

我们没有太多数据要处理,它主要是一个请求-响应后端应用程序,因此我们不使用parallelStreams。我知道我们应该使用自定义执行器,谢谢!因此,如果10个用户使用具有10个可完成未来的加载方法,那么将创建其中100个,并且一次只处理3个?等等,这是否意味着如果主机只有一个处理器,默认线程池将没有线程?听起来很糟糕。但是我知道什么呢?也许再也没有只有一个处理器的主机了。@james有一台核心机器