Custom thread pool in Java 8 parallel stream

后端 未结 15 911
旧巷少年郎
旧巷少年郎 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 01:16

    If you don't mind using a third-party library, with cyclops-react you can mix sequential and parallel Streams within the same pipeline and provide custom ForkJoinPools. For example

     ReactiveSeq.range(1, 1_000_000)
                .foldParallel(new ForkJoinPool(10),
                              s->s.filter(i->true)
                                  .peek(i->System.out.println("Thread " + Thread.currentThread().getId()))
                                  .max(Comparator.naturalOrder()));
    

    Or if we wished to continue processing within a sequential Stream

     ReactiveSeq.range(1, 1_000_000)
                .parallel(new ForkJoinPool(10),
                          s->s.filter(i->true)
                              .peek(i->System.out.println("Thread " + Thread.currentThread().getId())))
                .map(this::processSequentially)
                .forEach(System.out::println);
    

    [Disclosure I am the lead developer of cyclops-react]

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

    The parallel streams use the default ForkJoinPool.commonPool which by default has one less threads as you have processors, as returned by Runtime.getRuntime().availableProcessors() (This means that parallel streams use all your processors because they also use the main thread):

    For applications that require separate or custom pools, a ForkJoinPool may be constructed with a given target parallelism level; by default, equal to the number of available processors.

    This also means if you have nested parallel streams or multiple parallel streams started concurrently, they will all share the same pool. Advantage: you will never use more than the default (number of available processors). Disadvantage: you may not get "all the processors" assigned to each parallel stream you initiate (if you happen to have more than one). (Apparently you can use a ManagedBlocker to circumvent that.)

    To change the way parallel streams are executed, you can either

    • submit the parallel stream execution to your own ForkJoinPool: yourFJP.submit(() -> stream.parallel().forEach(soSomething)).get(); or
    • you can change the size of the common pool using system properties: System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "20") for a target parallelism of 20 threads. However, this no longer works after the backported patch https://bugs.openjdk.java.net/browse/JDK-8190974.

    Example of the latter on my machine which has 8 processors. If I run the following program:

    long start = System.currentTimeMillis();
    IntStream s = IntStream.range(0, 20);
    //System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "20");
    s.parallel().forEach(i -> {
        try { Thread.sleep(100); } catch (Exception ignore) {}
        System.out.print((System.currentTimeMillis() - start) + " ");
    });
    

    The output is:

    215 216 216 216 216 216 216 216 315 316 316 316 316 316 316 316 415 416 416 416

    So you can see that the parallel stream processes 8 items at a time, i.e. it uses 8 threads. However, if I uncomment the commented line, the output is:

    215 215 215 215 215 216 216 216 216 216 216 216 216 216 216 216 216 216 216 216

    This time, the parallel stream has used 20 threads and all 20 elements in the stream have been processed concurrently.

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

    There actually is a trick how to execute a parallel operation in a specific fork-join pool. If you execute it as a task in a fork-join pool, it stays there and does not use the common one.

    final int parallelism = 4;
    ForkJoinPool forkJoinPool = null;
    try {
        forkJoinPool = new ForkJoinPool(parallelism);
        final List<Integer> primes = forkJoinPool.submit(() ->
            // Parallel task here, for example
            IntStream.range(1, 1_000_000).parallel()
                    .filter(PrimesPrint::isPrime)
                    .boxed().collect(Collectors.toList())
        ).get();
        System.out.println(primes);
    } catch (InterruptedException | ExecutionException e) {
        throw new RuntimeException(e);
    } finally {
        if (forkJoinPool != null) {
            forkJoinPool.shutdown();
        }
    }
    

    The trick is based on ForkJoinTask.fork which specifies: "Arranges to asynchronously execute this task in the pool the current task is running in, if applicable, or using the ForkJoinPool.commonPool() if not inForkJoinPool()"

    0 讨论(0)
提交回复
热议问题