假设lambda表达式消耗了一定数量的资源(如内存),这是有限的并且需要限制并发执行的数量(例如:如果lambda暂时消耗100 MB(本地内存)并且我们希望限制它为1GB,我们不允许更多的10个并发评估。)
限制并发执行次数的最佳方法是什么,比如在
中IntStream.range(0, numberOfJobs).parallel().foreach( i -> { /*...*/ });
注意:显而易见的选择是执行类似
的嵌套 double jobsPerThread = (double)numberOfJobs / numberOfThreads;
IntStream.range(0, numberOfThreads).parallel().forEach( threadIndex ->
IntStream.range((int)(threadIndex * jobsPerThread), (int)((threadIndex+1) * jobsPerThread)).sequential().forEach( i -> { /*...*/ }));
这是唯一的方法吗? Tt不是 优雅。其实我想要一个
IntStream.range(0, numberOfJobs).parallel(numberOfThreads).foreach( i -> { /*...*/ });
答案 0 :(得分:4)
Stream
使用ForkJoinPool
进行并行操作。默认情况下,它们使用ForkJoinPool.commonPool()
,后者不允许更改并发。但是,您可以使用自己的ForkJoinPool
实例。当您在自己的ForkJoinPool
上下文中执行流代码时,此上下文池将用于流操作。以下示例通过使用默认行为执行相同的操作一次并使用具有2
的固定并发性的自定义池来说明这一点:
import java.util.HashSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.IntStream;
public class InterfaceStaticMethod {
public static void main(String[] arg) throws Exception {
Runnable parallelCode=() -> {
HashSet<String> allThreads=new HashSet<>();
IntStream.range(0, 1_000_000).parallel().filter(i->{
allThreads.add(Thread.currentThread().getName()); return false;}
).min();
System.out.println("executed by "+allThreads);
};
System.out.println("default behavior: ");
parallelCode.run();
System.out.println("specialized pool:");
ForkJoinPool pool=new ForkJoinPool(2);
pool.submit(parallelCode).get();
}
}
答案 1 :(得分:2)
根据您的使用情况,使用CompletableFuture
实用程序方法可能更容易:
import static java.util.concurrent.CompletableFuture.runAsync;
ExecutorService executor = Executors.newFixedThreadPool(10); //max 10 threads
for (int i = 0; i < numberOfJobs; i++) {
runAsync(() -> /* do something with i */, executor);
}
//or with a stream:
IntStream.range(0, numberOfJobs)
.forEach(i -> runAsync(() -> /* do something with i */, executor));
与您的代码的主要区别在于并行forEach
只会在上一个作业结束后返回,而runAsync
将在所有作业提交后立即返回。如果需要,有多种方法可以改变这种行为。