如何(全局)替换Java并行流的公共线程池后端?

时间:2014-05-09 11:59:41

标签: java multithreading concurrency parallel-processing stream

我想全局替换Java并行流默认使用的公共线程池,例如

IntStream.range(0,100).parallel().forEach(i -> {
    doWork();
});

我知道可以通过向专用线程池提交此类指令来使用专用的ForkJoinPool(请参阅Custom thread pool in Java 8 parallel stream)。这里的问题是

  • 是否可以通过其他实现替换常见的ForkJoinPool(比如Executors.newFixedThreadPool(10)
  • 是否可以通过某些全局设置来实现,例如,某些JVM属性?

备注:我之所以要更换F / J池,是因为它似乎有一个错误,使其无法用于嵌套的并行循环。

嵌套并行循环的性能很差,可能会导致死锁,请参阅http://christian-fries.de/blog/files/2014-nested-java-8-parallel-foreach.html

例如:以下代码导致死锁:

// Outer loop
IntStream.range(0,24).parallel().forEach(i -> {

    // (omitted:) do some heavy work here (consuming majority of time)

    // Need to synchronize for a small "subtask" (e.g. updating a result)
    synchronized(this) {
        // Inner loop (does s.th. completely free of side-effects, i.e. expected to work)
        IntStream.range(0,100).parallel().forEach(j -> {
            // do work here
        });
    }
});

(即使没有任何额外的代码,“在这里工作”,假设并行性设置为< 12)。

我的问题是如何更换FJP。如果您想讨论嵌套并行循环,可以检查Nested Java 8 parallel forEach loop perform poor. Is this behavior expected?

2 个答案:

答案 0 :(得分:5)

我认为这不是流API的使用方式。似乎你(错误地)使用它来执行并行任务执行(专注于任务,而不是数据),而不是进行并行流处理(专注于流中的数据)。您的代码以某种方式违反了流的一些主要原则。 (我以某种方式写作'因为它并不是真的被禁止但是气馁):避免states and side effects

除此之外(或者可能是因为副作用),你在外循环中使用了大量同步,这是其他一切,但无害!

虽然文档中没有提到,但并行流在内部使用common ForkJoinPool。无论这是否缺乏文件,我们都必须接受这一事实。 JavaDoc of ForkJoinTask州:

  

可以定义和使用可能阻塞的ForkJoinTasks,但这样做需要进一步考虑:(1)如果任何其他任务应该依赖于阻止外部同步或I / O的任务,则完成很少。从未加入的事件类型异步任务(例如,那些子类化CountedCompleter)通常属于此类别。 (2)为了尽量减少资源影响,任务应该很小;理想情况下只执行(可能)阻止操作。 (3)除非使用ForkJoinPool.ManagedBlocker API,或者已知可能被阻塞的任务的数量小于池的ForkJoinPool.getParallelism级别,否则池不能保证有足够的线程可用于确保进度或表现不错。

同样,您似乎正在使用流来替代简单的for循环和执行服务。

  • 如果您只想并行执行n个任务,请使用ExecutionService
  • 如果您有一个更复杂的示例,其中任务正在创建子任务,请考虑使用ForkJoinPool(使用ForkJoinTasks)。 (它确保了一定数量的线程,没有死锁的危险,因为等待其他任务完成的任务太多,因为等待的任务不会阻塞它们的执行线程。)
  • 如果要(并行)处理数据,请考虑使用流API。
  • 你无法安装'自定义common pool。它是在私有静态代码内部创建的。
  • 但您可以使用某些系统属性对公共池的并行性,线程工厂和异常处理程序产生影响(请参阅JavaDoc of ForkJoinPool

不要混淆ExecutionServiceForkJoinPool。他们(通常)不是彼此的替代品!

答案 1 :(得分:1)

虽然您的原始问题已经得到了很好的回答 isnot2bad,对于您而言,所描述的错误(您希望交换FJP实现的原因)似乎可以通过java 1.8.0.40修复。见Nested Java 8 parallel forEach loop perform poor. Is this behavior expected?