Java并发迭代:每个项目的分而治之与可运行

Java并发迭代:每个项目的分而治之与可运行,java,multithreading,concurrency,Java,Multithreading,Concurrency,当我有数百个项目需要迭代时,我必须对每个项目进行大量计算,我会采取“分而治之”的方法。基本上,我将处理器计数+1,并将这些项目划分为相同数量的批。然后我将在缓存线程池中的可运行线程上执行每个批处理。它似乎工作得很好。我的GUI任务从20秒变为2秒,这对用户来说是一个更好的体验 然而,我正在读Brian Goetz关于并发性的好书,我注意到,对于遍历项目列表,他会采取完全不同的方法。他会为每一个项目启动一个Runnable!以前,我总是猜测这会很糟糕,尤其是在缓存线程池上,它可能会创建大量线程。但

当我有数百个项目需要迭代时,我必须对每个项目进行大量计算,我会采取“分而治之”的方法。基本上,我将处理器计数+1,并将这些项目划分为相同数量的批。然后我将在缓存线程池中的可运行线程上执行每个批处理。它似乎工作得很好。我的GUI任务从20秒变为2秒,这对用户来说是一个更好的体验

然而,我正在读Brian Goetz关于并发性的好书,我注意到,对于遍历项目列表,他会采取完全不同的方法。他会为每一个项目启动一个Runnable!以前,我总是猜测这会很糟糕,尤其是在缓存线程池上,它可能会创建大量线程。但是,在更大的范围内,每个runnable可能会很快完成,我知道缓存线程池对于短任务来说是非常理想的


那么,在计算量大的项目中进行迭代,哪一种更被接受?分成固定数量的批次,并给每个批次一个可运行的?还是在自己的runnable中启动每个项目?如果后一种方法是最优的,那么可以使用缓存线程池还是使用有界线程池更好?

对于批处理,您将始终必须等待运行时间最长的批处理(您的速度与最慢的批处理一样快)。“分而治之”意味着管理开销:为分而治之进行管理,并监控征服

为每个项目创建一个任务相对简单(无需管理),但您是对的,因为它可能会启动数百个线程(不太可能,但可能会发生),如果该任务不进行/很少进行I/O,并且大部分是CPU密集型的,那么这只会降低速度(上下文切换)

如果缓存的线程池没有启动数百个线程(请参阅),那么一定要使用缓存的线程池。如果启动的线程太多,那么另一种选择是使用有边界的线程池。但是有界线程池需要一些调整/决策:例如,您是使用无界任务队列还是有界任务队列


另一方面:还有一种方法适用于启动子任务的任务。

批处理的缺点是,如果一个批处理比其他批处理花费的时间更长,那么您就浪费了时间,因为只有一个CPU在完成其工作时会很忙。这就是为什么使用单个可运行程序更好,但我建议使用固定大小的线程池。如果您的任务纯粹是计算任务,没有I/O,那么创建多个线程就没有什么好处(其他线程可以进行计算的CPU空闲时间就没有了),所以将线程池固定在[processor count]线程上。是的,F/J更容易。对于批量大小,可以将阈值设置为一个较小的数字。不是每个元素一个任务(可能意味着很多任务)放进两个、三个等等,然后进行一些实验。我一直听说ForkJoin,现在听起来我可能对它有实际用途。它有它的问题,但对于简单的拆分和只执行计算任务来说,它工作得很好。太糟糕了,我的公司仍然使用Java 6,即使没有很好的理由。Id使用F/Jotherwise@ThomasN. Java 6不一定是个问题: