Java parallelStream未使用预期的线程数

Java parallelStream未使用预期的线程数,java,multithreading,java-8,java-stream,Java,Multithreading,Java 8,Java Stream,Java8ParallelStream使用的线程似乎比系统属性Java.util.concurrent.ForkJoinPool.common.parallelism指定的线程多。这些单元测试表明,我使用自己的ForkJoinPool使用所需的线程数处理任务,但使用parallelStream时,线程数高于预期 import org.junit.Test; import java.util.ArrayList; import java.util.List; import java.util.c

Java8ParallelStream使用的线程似乎比系统属性Java.util.concurrent.ForkJoinPool.common.parallelism指定的线程多。这些单元测试表明,我使用自己的ForkJoinPool使用所需的线程数处理任务,但使用parallelStream时,线程数高于预期

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.Assert.assertTrue;

public class ParallelStreamTest {

    private static final int TOTAL_TASKS = 1000;

    @Test
    public void testParallelStreamWithParallelism1() throws InterruptedException {
        final Integer maxThreads = 1;
        System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", maxThreads.toString());
        List<Integer> objects = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            objects.add(i);
        }

        final AtomicInteger concurrentThreads = new AtomicInteger(0);
        final AtomicInteger taskCount = new AtomicInteger(0);

        objects.parallelStream().forEach(i -> {
            processTask(concurrentThreads, maxThreads); //expected to be called one at the time
            taskCount.addAndGet(1);
        });

        assertTrue(taskCount.get() == TOTAL_TASKS);
    }

    @Test
    public void testMyOwnForkJoinPoolWithParallelism1() throws InterruptedException {
        final Integer threads = 1;
        List<Integer> objects = new ArrayList<>();
        for (int i = 0; i < TOTAL_TASKS; i++) {
            objects.add(i);
        }

        ForkJoinPool forkJoinPool = new ForkJoinPool(1);
        final AtomicInteger concurrentThreads = new AtomicInteger(0);
        final AtomicInteger taskCount = new AtomicInteger(0);

        forkJoinPool.submit(() -> objects.parallelStream().forEach(i -> {
            processTask(concurrentThreads, threads); //expected to be called one at the time
            taskCount.addAndGet(1);
        }));
        forkJoinPool.shutdown();
        forkJoinPool.awaitTermination(1, TimeUnit.MINUTES);

        assertTrue(taskCount.get() == TOTAL_TASKS);
    }

    /**
     * It simply processes a task increasing first the concurrentThreads count
     *
     * @param concurrentThreads Counter for threads processing tasks
     * @param maxThreads Maximum number of threads that are expected to be used for processing tasks
     */
    private void processTask(AtomicInteger concurrentThreads, int maxThreads) {
        int currentConcurrentThreads = concurrentThreads.addAndGet(1);
        if (currentConcurrentThreads > maxThreads) {
            throw new IllegalStateException("There should be no more than " + maxThreads + " concurrent thread(s) but found " + currentConcurrentThreads);
        }

        // actual processing would go here

        concurrentThreads.decrementAndGet();
    }
}
import org.junit.Test;
导入java.util.ArrayList;
导入java.util.List;
导入java.util.concurrent.ForkJoinPool;
导入java.util.concurrent.TimeUnit;
导入java.util.concurrent.AtomicInteger;
导入静态org.junit.Assert.assertTrue;
公共类并行测试{
私有静态最终整数总任务=1000;
@试验
public void testParallelStreamWithParallelism1()引发InterruptedException{
最终整数maxThreads=1;
setProperty(“java.util.concurrent.ForkJoinPool.common.parallelism”,maxThreads.toString());
列表对象=新的ArrayList();
对于(int i=0;i<1000;i++){
增加(i);
}
最终AtomicInteger concurrentThreads=新的AtomicInteger(0);
最终AtomicInteger任务计数=新的AtomicInteger(0);
objects.parallelStream().forEach(i->{
processTask(concurrentThreads,maxThreads);//预期一次调用一个
taskCount.addAndGet(1);
});
assertTrue(taskCount.get()=总任务数);
}
@试验
public void testMyOwnForkJoinPoolWithParallelism1()引发InterruptedException{
最终整数线程=1;
列表对象=新的ArrayList();
对于(int i=0;iobjects.parallelStream().forEach(i->{
processTask(concurrentThreads,threads);//预期一次调用一个
taskCount.addAndGet(1);
}));
forkJoinPool.shutdown();
forkJoinPool.等待终止(1,时间单位:分钟);
assertTrue(taskCount.get()=总任务数);
}
/**
*它只是处理一个任务,首先增加concurrentThreads计数
*
*@param concurrentThreads用于线程处理任务的计数器
*@param maxThreads预期用于处理任务的最大线程数
*/
私有void processTask(AtomicInteger concurrentThreads,int-maxThreads){
int currentConcurrentThreads=concurrentThreads.addAndGet(1);
如果(currentConcurrentThreads>maxThreads){
抛出新的IllegalStateException(“应不超过“+maxThreads+”个并发线程,但找到“+currentConcurrentThreads”);
}
//实际处理将在这里进行
concurrentThreads.decrementAndGet();
}
}
应该只有一个线程用于处理任务,因为ForkJoinPool具有
parallelism=1
java.util.concurrent.ForkJoinPool.common.parallelism=1
。因此,这两个测试都应通过,但testParallelStreamWithParallelism1在以下情况下失败:

java.lang.IllegalStateException:应不超过1个并发线程,但找到2个

似乎设置java.util.concurrent.ForkJoinPool.common.parallelism=1未按预期工作,同时处理了多个并发任务


有什么想法吗?

你删除了这个问题的第一篇帖子中的正确答案,因此我将对此进行详细阐述。你的问题是:
int currentConcurrentThreads=concurrentThreads.addAndGet(1)
在这里:

objects.parallelStream().forEach(i -> {
  processTask(concurrentThreads, maxThreads); //expected to be called one at the time
  taskCount.addAndGet(1);
});
并行流中的每个线程调用
processTask
。因此,每个线程都会增加concurrentThreads(但由于某些原因,不使用
).由于每个线程都并行运行,因此在任何线程减量之前,它们都会先递增
concurrentThreads
。因此,您当然超过了预期的线程数。

同时设置此参数:

    System.setProperty("java.util.concurrent.ForkJoinPool.common.maximumSpares", "0");

这对我有用。显然(虽然没有很好的文档记录),有允许的“备用”线程从默认的ForkJoinPool拾取工作

Fork/Join池的并行度设置决定了池工作线程的数量,但由于调用线程(例如主线程)也将处理作业,因此在使用公共池时,始终会有一个多线程。这就是为什么要获得与内核数相等的实际工作线程数

对于自定义Fork/Join池,流操作的调用线程已经是池的工作线程,因此,将其用于处理作业不会增加工作线程的实际数量

必须强调的是,流实现和Fork/Join池之间的交互是完全未指定的,因为流在后台使用Fork/Join框架是一个实现细节。无法保证更改默认池的属性对流有任何影响,也不能保证从自定义分叉/联接池的任务中调用流操作将使用该自定义池。

运行此示例:

  IntStream.rangeClosed(0,9).parallel().forEach((i) -> {
      try {
        System.out.println("id - " + Thread.currentThread().getName());
      } catch (Exception e) {
      }
    });
当您使用参数java.util.concurrent.ForkJoinPool.common.parallelism=1时,您将看到如下内容

id - main
id - main
id - ForkJoinPool.commonPool-worker-1
id - main
id - ForkJoinPool.commonPool-worker-1
id - main
id - ForkJoinPool.commonPool-worker-1
id - main
id - ForkJoinPool.commonPool-worker-1
id - ForkJoinPool.commonPool-worker-1

正如您现在所知,流使用公共ForkJoinPool(paralelism=1),此外,它们还使用当前线程。

您的问题是,“
testParallelStreamWithParallelism1
失败”,但您的注释是,“
testParallelStreamWithParallelism1
…按预期通过”。它是哪一个?这仍然不能回答并行流最初是如何使用多个线程的问题。在t处,应该只有一个线程处理一个任务