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
ForkJoinPool性能Java 8 vs 11_Java_Multithreading_Java 8_Java 11 - Fatal编程技术网

ForkJoinPool性能Java 8 vs 11

ForkJoinPool性能Java 8 vs 11,java,multithreading,java-8,java-11,Java,Multithreading,Java 8,Java 11,考虑以下代码: package com.sarvagya; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ForkJoinPool; import java.util.stream.Collectors; public class Streamer { private static f

考虑以下代码:

package com.sarvagya;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors;

public class Streamer {
    private static final int LOOP_COUNT  = 2000;
    public static void main(String[] args){
        try{
            for(int i = 0; i < LOOP_COUNT; ++i){
                poolRunner();
                System.out.println("done loop " + i);
                try{
                    Thread.sleep(50L);
                }
                catch (Exception e){
                    System.out.println(e);
                }
            }
        }
        catch (ExecutionException | InterruptedException e){
            System.out.println(e);
        }

        // Add a delay outside the loop to make sure all daemon threads are cleared before main exits.
        try{
            Thread.sleep(10 * 60 * 1000L);
        }
        catch (Exception e){
            System.out.println(e);
        }
    }

    /**
     * poolRunner method.
     * Assume I don't have any control over this method e.g. done by some library.
     * @throws InterruptedException
     * @throws ExecutionException
     */
    private static void poolRunner() throws InterruptedException, ExecutionException {
        ForkJoinPool pool = new ForkJoinPool();
        pool.submit(() ->{
            List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,7,8,9,10, 11,12,14,15,16);
            List<Integer> collect = numbers.stream()
                    .parallel()
                    .filter(xx -> xx > 5)
                    .collect(Collectors.toList());
            System.out.println(collect);
        }).get();
    }
}
它很快就达到了最大线程限制。将LOOP_COUNT保持在500,效果很好,但是,这些线程的清除速度非常慢,达到大约500个线程的平台。见下图:

图:OpenJDK 11中的线程信息

图:OpenJDK 11中的评测

线程在jdk8中停止,但在jdk11中等待。在Java11中,守护进程线程的数量也应该减少,但是,它的速度很慢,不能像预期的那样工作。此外,假设我不能控制
poolRunner
方法。这种方法是由一些外部库提供的。
这是OpenJDK 11的问题,还是我在代码中做了一些错误的事情。谢谢。

你做得不对

在上面的代码中,我创建了一个
ForkJoinPool
,并向其提交了一些任务

实际上,您正在创建2000个
ForkJoinPool
实例

不要这样做,您应该创建一个单独的
ForkJoinPool
,具有适合手头任务的并行度(即线程数)


创建大量(即数千)线程是一个非常糟糕的主意。即使您可以在不触发OOME的情况下执行此操作,也将消耗大量堆栈和堆内存,并在调度程序和垃圾收集器上放置大量负载。。。没有真正的好处。

您的代码正在创建大量的
ForkJoinPool
实例,并且在任何池使用后都不会调用
shutdown()
。因为在Java8的情况下,规范中没有任何东西保证工作线程将终止,所以此代码甚至可能以2000结尾(⟨池数⟩) 时代⟨芯数⟩ 线程

在实践中,观察到的行为源于两秒钟的延迟。请注意,根据注释,超时的结果是试图减少工作线程的数量,这与仅终止不同。因此,如果n个线程经历超时,则并非所有n个线程都会终止,但线程的数量会减少一个,并且剩余的线程可能会再次等待。此外,短语“初始超时值”已经提示了它,每次实际超时都会增加。因此,n个空闲工作线程需要
n*(n+1)
秒才能因此(未记录的)超时而终止

从Java 9开始,有一个可配置的keepAliveTime,可以在of
ForkJoinPool
中指定,它还记录了默认值:

keepAliveTime
自上次使用以来线程终止(然后在需要时替换)所用的时间。对于默认值,请使用
60,TimeUnit.SECONDS
。 此文档可能会误导人们认为,当keepAliveTime处于空闲状态时,现在所有工作线程可能会一起终止,但实际上,仍然存在一种行为,即一次只将池缩小一个,尽管现在,时间没有增加。因此,n个空闲工作线程终止需要
60*n
秒因为之前的行为没有具体说明,所以它甚至不是不相容的

必须强调的是,即使使用相同的超时行为,所产生的最大线程数也可能会发生变化,因为具有更好代码优化的较新JVM会减少实际操作的执行时间(无需人工插入
Thread.sleep(…)
)它将更快地创建新线程,而终止时间仍然与挂钟时间有关


需要注意的是,当您知道不再需要线程池时,您不应该依赖自动工作线程终止。相反,您应该在完成后调用
shutdown()


您可以使用以下代码验证该行为:

int threadNumber = 8;
ForkJoinPool pool = new ForkJoinPool(threadNumber);
// force the creation of all worker threads
pool.invokeAll(Collections.nCopies(threadNumber*2, () -> { Thread.sleep(500); return ""; }));
int oldNum = pool.getPoolSize();
System.out.println(oldNum+" threads; waiting for dying threads");
long t0 = System.nanoTime();
while(oldNum > 0) {
    while(pool.getPoolSize()==oldNum)
        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(200));
    long t1 = System.nanoTime();
    oldNum = pool.getPoolSize();
    System.out.println(threadNumber-oldNum+" threads terminated after "
        +TimeUnit.NANOSECONDS.toSeconds(t1 - t0)+"s");
}
Java 8:
8个线程;正在等待死亡线程
1个线程在2s后终止
2个线程在6s后终止
12秒后终止的3个线程
4个线程在20秒后终止
30秒后终止的5个线程
42s后终止的6个线程
56s后终止的7个线程
72s后终止的8个线程
Java 11:
8个线程;正在等待死亡线程
1线程在60秒后终止
120秒后终止的2个线程
180s后终止的3个线程
240秒后终止的4个线程
300秒后终止的5个线程
360s后终止的6个线程
420s后终止的7个线程

从未完成,显然,至少有最后一个工作线程保持活动状态

Stephen,问题是我无法控制
poolRunner
方法。假设这来自某个库,该库内部使用ForkJoinPool,但我将循环此方法。抱歉,在这种情况下没有解决方案。如果是这样的话问题,他们如果你想要一个解决方案,你需要改变问题。我不知道。但是我认为如果你用正确的方式做事情并不重要。代码不仅创建了大量的池,而且忘记调用
shutdown()
在每个池使用后,规范中的任何内容都不能保证工作线程会终止。因此,对于16核机器,它可以创建32000个永不消亡的线程,所有这些线程都在规范范围内。我们之所以不允许并行流在指定池中运行,而是始终在公共池中运行,这是有原因的-为了防止像作者在这里采取的极端措施那样射中你自己的脚。
poolRunner()
的作者认为他们通过“超越”运行时,剪切和粘贴“变通方法”是聪明的他们不理解的堆栈溢出代码。此代码已被终止;您唯一的选择是不调用它。您确定除了JDK版本外,您的测试条件完全相同吗?我看到不同的max heap,并且应用程序名称似乎与您的2个分析快照不同相同的代码是bei
int threadNumber = 8;
ForkJoinPool pool = new ForkJoinPool(threadNumber);
// force the creation of all worker threads
pool.invokeAll(Collections.nCopies(threadNumber*2, () -> { Thread.sleep(500); return ""; }));
int oldNum = pool.getPoolSize();
System.out.println(oldNum+" threads; waiting for dying threads");
long t0 = System.nanoTime();
while(oldNum > 0) {
    while(pool.getPoolSize()==oldNum)
        LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(200));
    long t1 = System.nanoTime();
    oldNum = pool.getPoolSize();
    System.out.println(threadNumber-oldNum+" threads terminated after "
        +TimeUnit.NANOSECONDS.toSeconds(t1 - t0)+"s");
}