为什么这个 ConcurrentHashMap 一次只流 运行 一半的条目?

Why does this ConcurrentHashMap stream only run half of entries at a time?

我被这种好奇心迷住了。 (我使用的是 ConcurrentHashMap 而不是 ConcurrentSkipListSet,因为 class 没有实现 Comparable。)我的计算机上有很多可用的 CPU,classes 之间没有区别 运行 在流中(随机数生成除外)。令人怀疑的是偶数 运行 首先(始终如一)。

这是 nRuns=10 时的代码和输出。我希望所有 10 个线程同时启动和 运行(就像我在其他 ConcurrentHashMap 中通常做的那样)。可能是由于 SvmCrossValidator 调用了 LIBSVM 中的一些静态代码吗?这就是我能想到的。在我看来,从基本 Java 的角度来看,这个流应该同时启动所有 10 个进程。

// instantiate and run nRuns times
ConcurrentHashMap<Integer,SvmCrossValidator> scvMap = new ConcurrentHashMap<>();
for (int i=0; i<nRuns; i++) {
    scvMap.put(i, new SvmCrossValidator(param, nrFold, inputFilename, nCases, nControls));
}
// parallel stream
scvMap.entrySet().parallelStream().forEach(entry -> {
        System.err.println("SVM run "+entry.getKey()+" started.");
        entry.getValue().run();
        System.err.println("SVM run "+entry.getKey()+" finished.");
    });

输出:

SVM run 2 started.
SVM run 0 started.
SVM run 6 started.
SVM run 4 started.
SVM run 8 started.

在这前五个磨掉的时候在这里等待很长时间...

SVM run 8 finished.
SVM run 9 started.
SVM run 6 finished.
SVM run 7 started.
SVM run 0 finished.
SVM run 1 started.
SVM run 2 finished.
SVM run 3 started.
SVM run 4 finished.
SVM run 5 started.
SVM run 9 finished.
SVM run 1 finished.
SVM run 7 finished.
SVM run 3 finished.
SVM run 5 finished.

我认为有两件事影响了这一点。首先将线程名称添加到您的 System.out 以使工作线程更清晰:

System.err.println("SVM run "+entry.getKey()+" started." +' '+Thread.currentThread().getName());

系统 属性 java.util.concurrent.ForkJoinPool.common.parallelism 影响 ForkJoinPool 可用的执行队列 - 请参阅 ForkJoinPool 的构造函数或 javadoc。

private ForkJoinPool(byte forCommonPoolOnly)

但是 parallelStream() 创建了一个拆分器,我认为它也会根据内容的大小做出选择,这也决定了流的数量 - 无论 ForkJoinPool 的大小如何。

更改 java.util.concurrent.ForkJoinPool.common.parallelism 可能不会影响结果,除非您使 nRuns 更大,然后它使用更多的 ForkJoinPool.commonPool-worker 线程。

所以在我的机器上进行了一些测试:

  • nRuns=10 即使在 parallel=128 的情况下也从未使用超过 5 个工作线程,甚至重复使用相同的工作线程,即使有更多可用线程
  • nRuns=1000 - 在 parallel=128 的情况下同时达到 130 左右。请注意,并行度不是用于工作线程数的值。