我读了一篇关于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的生活代码)
解决方法:
虽然ForkJoinPool实现了ExecutorService,但它在概念上与“普通”执行器不同.
如果您的任务产生更多任务并等待它们完成,您可以轻松地看到差异,例如:通过电话
executor.invoke(new Task()); // blocks this thread until new task completes
在正常执行程序服务中,等待其他任务完成将阻止当前线程.有两种可能的结果:如果执行程序服务具有固定数量的线程,则如果最后一个正在运行的线程等待另一个任务完成,则可能会死锁.如果您的执行程序根据需要动态创建新线程,则线程数可能会爆炸,并且最终会有数千个可能导致饥饿的线程.
相反,fork / join框架在此期间重用该线程来执行其他任务,因此尽管线程数是固定的,它也不会死锁:
new MyForkJoinTask().invoke();
因此,如果您遇到可以递归求解的问题,可以考虑使用ForkJoinPool,因为您可以轻松地将一个递归级别实现为ForkJoinTask.
只需检查示例中正在运行的线程数.