parallelStream中的线程大小大于cpu内核

时间:2019-12-24 09:08:44

标签: java

默认情况下,parallelStream中commonPool的大小应为cpu_cores - 1

但是,在我的应用程序中,它总是大于硬件cpu_cores

VisualVM屏幕截图:

enter image description here

太困惑了,我已经搜索了一半的互联网,但是找不到答案。

我的配置:

Runtime.getRuntime().availableProcessors()=12

java.util.concurrent.ForkJoinPool.common.parallelism=null(默认设置)

我的代码:

            final CountDownLatch countDownLatch = new CountDownLatch(tempList.size());
            tempList.parallelStream().forEach(om -> {
                countDownLatch.countDown();
                redisReBloomService.add(config.getRedisKey(), om.getChannelNo());
            });
            countDownLatch.await();

此外,我尝试了自定义池设置,但也不起作用-

ForkJoinPool forkJoinPool = new ForkJoinPool(3);  
forkJoinPool.submit(() -> {  
    tempList.parallelStream().forEach(om -> {
        countDownLatch.countDown();
        redisReBloomService.add(config.getRedisKey(), om.getChannelNo());
    }).get();
});

一些信息: https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.html Custom thread pool in Java 8 parallel stream

1 个答案:

答案 0 :(得分:3)

ForkJoinPool中的并行度不是池中最大线程数。它是活动线程的目标。如果某些线程被阻塞,则池可能会启动新线程以达到所需的并行度。

来自ForkJoinPool的文档:

  

即使某些任务因等待加入其他任务而停滞不前,该池也会尝试通过动态添加,暂停或恢复内部工作线程来维护足够的活动(或可用)线程。但是,面对阻塞的I / O或其他不受管理的同步,不能保证这样的调整。嵌套的ForkJoinPool.ManagedBlocker接口可扩展所容纳的同步类型。

该屏幕快照显示,当其他线程切换到状态Monitor(粉红色的)时,新线程恰好在同一时间启动。我的猜测是,redisReBloomService.add(…)调用必须在该监视器上等待时在内部使用ManagedBlocker,从而导致池启动更多的工作线程。

这是一个使用ManagedBlocker的小例子,它演示了您观察到的类似行为。 ManagedBlocker睡眠1秒后,通常可以在VisualVM中观察到新的辅助线程。

public class ForkJoinPoolTest {

    @Test
    public void testManagedBlocker() throws InterruptedException {
        // wait to be able to connect with VisualVM
        Thread.sleep(10_000);

        IntStream.range(0, 100).parallel().peek(number -> {
                doWork();

                // Run a managed blocker some times.
                // Every time it blocks, a new worker thread might be started.
                if (ThreadLocalRandom.current().nextInt(10) == 0) {
                    try {
                        ForkJoinPool.managedBlock(new ManagedBlocker() {
                            @Override
                            public boolean block() throws InterruptedException {
                                Thread.sleep(1_000);
                                return true;
                            }

                            @Override
                            public boolean isReleasable() {
                                return false;
                            }
                        });
                    } catch (InterruptedException ignored) { }
                }
            })
            .sum();
    }

    /** Some CPU bound workload **/
    void doWork() {
        for (int i = 0; i < 1_000_000; i++) {
            Math.random();
        }
    }
}