Custom thread pool in Java 8 parallel stream

后端 未结 15 928
旧巷少年郎
旧巷少年郎 2020-11-22 00:15

Is it possible to specify a custom thread pool for Java 8 parallel stream? I can not find it anywhere.

Imagine that I have a server application and I would like to

相关标签:
15条回答
  • 2020-11-22 00:52

    Note: There appears to be a fix implemented in JDK 10 that ensures the Custom Thread Pool uses the expected number of threads.

    Parallel stream execution within a custom ForkJoinPool should obey the parallelism https://bugs.openjdk.java.net/browse/JDK-8190974

    0 讨论(0)
  • 2020-11-22 00:55

    We can change the default parallelism using the following property:

    -Djava.util.concurrent.ForkJoinPool.common.parallelism=16
    

    which can set up to use more parallelism.

    0 讨论(0)
  • 2020-11-22 00:55

    I tried the custom ForkJoinPool as follows to adjust the pool size:

    private static Set<String> ThreadNameSet = new HashSet<>();
    private static Callable<Long> getSum() {
        List<Long> aList = LongStream.rangeClosed(0, 10_000_000).boxed().collect(Collectors.toList());
        return () -> aList.parallelStream()
                .peek((i) -> {
                    String threadName = Thread.currentThread().getName();
                    ThreadNameSet.add(threadName);
                })
                .reduce(0L, Long::sum);
    }
    
    private static void testForkJoinPool() {
        final int parallelism = 10;
    
        ForkJoinPool forkJoinPool = null;
        Long result = 0L;
        try {
            forkJoinPool = new ForkJoinPool(parallelism);
            result = forkJoinPool.submit(getSum()).get(); //this makes it an overall blocking call
    
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            if (forkJoinPool != null) {
                forkJoinPool.shutdown(); //always remember to shutdown the pool
            }
        }
        out.println(result);
        out.println(ThreadNameSet);
    }
    

    Here is the output saying the pool is using more threads than the default 4.

    50000005000000
    [ForkJoinPool-1-worker-8, ForkJoinPool-1-worker-9, ForkJoinPool-1-worker-6, ForkJoinPool-1-worker-11, ForkJoinPool-1-worker-10, ForkJoinPool-1-worker-1, ForkJoinPool-1-worker-15, ForkJoinPool-1-worker-13, ForkJoinPool-1-worker-4, ForkJoinPool-1-worker-2]
    

    But actually there is a weirdo, when I tried to achieve the same result using ThreadPoolExecutor as follows:

    BlockingDeque blockingDeque = new LinkedBlockingDeque(1000);
    ThreadPoolExecutor fixedSizePool = new ThreadPoolExecutor(10, 20, 60, TimeUnit.SECONDS, blockingDeque, new MyThreadFactory("my-thread"));
    

    but I failed.

    It will only start the parallelStream in a new thread and then everything else is just the same, which again proves that the parallelStream will use the ForkJoinPool to start its child threads.

    0 讨论(0)
  • 2020-11-22 00:57

    Here is how I set the max thread count flag mentioned above programatically and a code sniped to verify that the parameter is honored

    System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "2");
    Set<String> threadNames = Stream.iterate(0, n -> n + 1)
      .parallel()
      .limit(100000)
      .map(i -> Thread.currentThread().getName())
      .collect(Collectors.toSet());
    System.out.println(threadNames);
    
    // Output -> [ForkJoinPool.commonPool-worker-1, Test worker, ForkJoinPool.commonPool-worker-3]
    
    0 讨论(0)
  • 2020-11-22 00:58

    Until now, I used the solutions described in the answers of this question. Now, I came up with a little library called Parallel Stream Support for that:

    ForkJoinPool pool = new ForkJoinPool(NR_OF_THREADS);
    ParallelIntStreamSupport.range(1, 1_000_000, pool)
        .filter(PrimesPrint::isPrime)
        .collect(toList())
    

    But as @PabloMatiasGomez pointed out in the comments, there are drawbacks regarding the splitting mechanism of parallel streams which depends heavily on the size of the common pool. See Parallel stream from a HashSet doesn't run in parallel .

    I am using this solution only to have separate pools for different types of work but I can not set the size of the common pool to 1 even if I don't use it.

    0 讨论(0)
  • 2020-11-22 01:02

    Alternatively to the trick of triggering the parallel computation inside your own forkJoinPool you can also pass that pool to the CompletableFuture.supplyAsync method like in:

    ForkJoinPool forkJoinPool = new ForkJoinPool(2);
    CompletableFuture<List<Integer>> primes = CompletableFuture.supplyAsync(() ->
        //parallel task here, for example
        range(1, 1_000_000).parallel().filter(PrimesPrint::isPrime).collect(toList()), 
        forkJoinPool
    );
    
    0 讨论(0)
提交回复
热议问题