Java8ForkJoinPool和Executors.newWorkStealingPool的详细区别?

Detailed difference between Java8 ForkJoinPool and Executors.newWorkStealingPool?

using的底层区别是什么:

ForkJoinPool = new ForkJoinPool(X);

ExecutorService ex = Executors.neWorkStealingPool(X);

其中 X 是所需的并行级别,即线程 运行..

根据文档,我发现它们很相似。还请告诉我,在任何正常使用情况下,哪个更合适、更安全。 我有 130 百万个条目要写入 BufferedWriter 并使用 Unix 按第一列排序对它们进行排序。

如果可能的话,也让我知道要保留多少线程。

注意: 我的系统有 8 个核心处理器和 32 GB 内存。

工作窃取是现代线程池用来减少工作队列争用的一种技术。

一个经典的线程池有一个队列,每个线程池线程锁定队列,出列一个任务,然后解锁队列。如果任务很短,而且数量很多,队列就会有很多争用。使用 无锁 队列在这里确实有帮助,但并不能完全解决问题。

现代线程池使用工作窃取 - 每个线程都有自己的队列。当一个线程池线程产生一个任务时——它将它排入他自己的队列。当线程池线程想要使任务出列时 - 它首先尝试从自己的队列中出列任务,如果没有 - 它 "steals" 从其他线程队列中工作。这确实减少了线程池的争用并提高了性能。

newWorkStealingPool 创建一个利用工作窃取的线程池,线程数与处理器数相同。

newWorkStealingPool 提出了一个新问题。如果我有四个逻辑核心,那么该池将总共有四个线程。如果我的任务阻塞——例如在同步 IO 上——我没有充分利用我的 CPUs。我想要的是在 任何给定时刻 的四个 活动 线程 - 例如 - 四个加密 AES 的线程和另外 140 个等待 IO 的线程完成。

这就是 ForkJoinPool 提供的 - 如果您的任务产生新任务并且该任务等待它们完成 - 池将注入新的活动线程以使 CPU 饱和。值得一提的是 ForkJoinPool 也利用工作窃取。

使用哪一个?如果您使用 fork-join 模型或者您知道您的任务无限期阻塞,请使用 ForkJoinPool。如果您的任务很短并且主要是 CPU-bound,请使用 newWorkStealingPool.

归根结底,现代应用程序倾向于使用具有可用处理器数量的线程池,并利用 异步 IO 无锁容器为了防止阻塞。这(通常)提供最佳性能。

newWorkStealingPoolForkJoinPool 的更高抽象级别。

如果您查看 Oracle jvm 实现,它只是一个预配置的 ForkJoinPool:

public static ExecutorService newWorkStealingPool() {
    return new ForkJoinPool(Runtime.getRuntime().availableProcessors(),
                            ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                            null, 
                            true);
}

不幸的是,查看实现并不是理解 class 目的的正确方法。

同时归功于:https://dzone.com/articles/diving-into-java-8s-newworkstealingpools

它只是 Fork/Join 框架的抽象...

/**
* Creates a work-stealing thread pool using all
* {@link Runtime#availableProcessors available processors}
* as its target parallelism level.
* @return the newly created thread pool
* @see #newWorkStealingPool(int)
* @since 1.8
*/
public static ExecutorService newWorkStealingPool() {
    return new ForkJoinPool(Runtime.getRuntime().availableProcessors(),
                            ForkJoinPool.defaultForkJoinWorkerThreadFactory,
                            null, true);
}