Java parallelStream未使用预期的线程数
Java8ParallelStream使用的线程似乎比系统属性Java.util.concurrent.ForkJoinPool.common.parallelism指定的线程多。这些单元测试表明,我使用自己的ForkJoinPool使用所需的线程数处理任务,但使用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
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处,应该只有一个线程处理一个任务