在并行流中限制并发评估数量(如使用fixedThreadPool)的最佳/最优雅方法是什么?

时间:2014-03-02 15:25:43

标签: java concurrency java-8

假设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 -> { /*...*/ });

2 个答案:

答案 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将在所有作业提交后立即返回。如果需要,有多种方法可以改变这种行为。