我读了一篇关于fork-join framework in Java 7的精彩文章,其想法是,使用ForkJoinPool
和ForkJoinTask
,池中的线程可以从其他任务中获取子任务,所以它能够使用更少的线程来处理更多的任务。
然后我尝试使用普通的ExecutorService
来做同样的工作,发现我无法区分,因为当我向池中提交新任务时,任务将在另一个可用的线程。
我能说的唯一区别是,如果我使用ForkJoinPool
,我不需要将池传递给任务,因为我可以调用task.fork()
使其在另一个线程上运行。但是对于普通ExecutorService
,我必须将池传递给任务,或者使其成为静态,因此在任务中,我可以调用pool.submit(newTask)
我错过了什么吗?
(您可以查看来自https://github.com/freewind/fork-join-test/tree/master/src的生活代码)
答案 0 :(得分:5)
尽管on
实现了ForkJoinPool
,但它在概念上与“普通”执行者不同。
如果您的任务产生更多任务并等待它们完成,您可以轻松地看到差异,例如:通过调用
ExecutorService
在正常执行程序服务中,等待其他任务完成将阻止当前线程。有两种可能的结果:如果执行程序服务具有固定数量的线程,则如果最后一个正在运行的线程等待另一个任务完成,则可能会死锁。如果您的执行程序根据需要动态创建新线程,则线程数可能会爆炸,并且您最终会有数千个可能导致饥饿的线程。
相反,fork / join框架在此期间重用该线程来执行其他任务,因此尽管线程数已修复,但它不会死锁:
executor.invoke(new Task()); // blocks this thread until new task completes
因此,如果您遇到可以递归求解的问题,请考虑使用new MyForkJoinTask().invoke();
,因为您可以轻松地将一级递归实现为ForkJoinPool
。
只需检查示例中正在运行的线程数。