代码之家  ›  专栏  ›  技术社区  ›  Coder-Man

具有completeTableFutures且没有自定义执行器的代码是否只使用等于核心数的线程数?

  •  4
  • Coder-Man  · 技术社区  · 8 年前

    我正在阅读Java 8的动作,第11章 CompletableFuture 这让我想到了我公司的代码库。

    行动手册中的Java 8说如果你有下面的代码,你只会使用4 复杂的未来 S一次(如果您有一台4核计算机)。这意味着,如果要异步执行例如10个操作,您将首先运行前4个 复杂的未来 s,然后是第二个4,然后是剩余的2个,因为默认 ForkJoinPool.commonPool() 仅提供等于 Runtime.getRuntime().availableProcessors() .

    在我公司的代码库中,有 @Service 调用的类 AsyncHelper s,其中包含一个方法 load() ,使用 复杂的未来 以不同的块异步加载有关产品的信息。我想知道它们一次是否只使用4个线程。

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

    有一个体系结构决定将PDP页面的细节分块加载。加载应该异步进行,当前代码使用 复杂的未来 让我们来看看伪代码:

    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
        }
    }
    

    如您所见,上面的代码不使用自定义执行器。

    这是否意味着如果加载方法有10个 复杂的未来 现在有2个人正在加载pdp页面,我们有20个人 复杂的未来 总共加载,然后全部加载20个 复杂的未来 不会一次全部执行,但一次只能执行4次?

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

    公共静态ForkJoinPool公用池() 返回公用池实例。此池是静态构造的;其运行状态不受尝试shutdown()或shutdownnow()的影响。但是,在程序system.exit(int)时,此池和任何正在进行的处理都会自动终止。任何依赖异步任务处理在程序终止前完成的程序都应在退出前调用CommonPool()。等待静止。

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

    2 回复  |  直到 8 年前
        1
  •  7
  •   Bohemian    8 年前

    是的,但比这更糟…

    公用池的默认大小为 1小于 处理器/核心的数量(如果只有1个处理器,则为1),因此您实际上正在处理 一次,不是4次。

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

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

        2
  •  4
  •   Solomon Slow    8 年前

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

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

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

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

    我不是这方面的专家,我不知道(除了做实验)如何找出线程的最佳数量,但是无论这个数字是多少,它都与系统拥有多少CPU无关,因此,您不应该使用该pu的默认池。常规用途。