100%CPU使用率的Java线程优化

我有一个应用程序接受队列上的工作,然后旋转,在独立的线程上完成。 线程数量不大,比如高达100,但这些都是密集型任务,可以快速将CPU提升到100%。

为了最快地完成大部分工作:我最好在我需要做更多工作时启动更multithreading,让Java线程调度程序处理分配工作,或者更聪明地管理工作负载以保持CPU低于100%让我更快?

这台机器专用于我的java应用程序。

编辑:

感谢您的精彩投入!

这些任务具有不同的复杂性并涉及I / O,因此具有4个低线程池可能只能将CPU运行到20%。 我无法知道有多少任务实际上将CPU带到100%。

我的想法是我应该通过RMI监控CPU并动态上下拨打工作,或者我应该不关心并让操作系统处理它。

如果在并行线程中有太多同时执行计算密集型任务,则会很快达到收益递减的程度。 实际上,如果有N个处理器(核心),那么你不需要超过N个这样的线程。 现在,如果任务偶尔会暂停I / O或用户交互,那么正确的数字可能会稍大一些。 但总的来说,如果在任何一个时刻有更多的线程想要进行计算而不是有可用的核心,那么你的程序就会浪费时间在上下文切换上 – 也就是说,调度会让你付出代价。

为了最快地完成大部分工作:我最好在我需要做更多工作时启动更multithreading,让Java线程调度程序处理分配工作,或者更聪明地管理工作负载以保持CPU低于100%让我更快?

随着您添加越来越多的线程,上下文切换,内存缓存刷新,内存缓存溢出以及内核和JVM线程管理所产生的开销也会增加。 当您的线程占用CPU时,它们的内核优先级降至最低,并且它们将达到时间片最小值。 随着越来越多的线程占用内存,它们会溢出各种内部CPU内存缓存。 CPU需要从较慢的内存中交换作业的可能性更高。 JVM内部存在更多的互斥锁本地争用,可能还有一些(可能是小的)增量的每线程和对象带宽GC开销。 根据用户任务的同步程度,更multithreading会导致内存刷新和锁争用增加。

对于任何程序和任何体系结构,线程都可以最佳地利用可用的处理器和IO资源,同时限制内核和JVM开销。 反复寻找最佳位置将需要多次迭代和一些猜测。

我建议使用Executors.newFixedThreadPool(SOME_NUMBER); 并向你提交工作。 然后,您可以执行多次运行,根据工作和框架的结构,上下调整线程数,直到找到同时运行的最佳池数。

但是要理解,最佳线程数将根据可能非常重要的处理器和其他因素而变化。 如果它们阻塞磁盘或网络IO资源,则可能需要更multithreading。 如果他们正在做的工作主要是基于CPU的,那么线程就会减少。

你的CPU以100%运行的事实并没有说明他们在做有用工作的忙碌程度。 在您的情况下,您使用的线程数多于核心数,因此100%包含一些上下文切换并且不必要地使用内存(对100个线程影响很小),这是次优的。

对于CPU密集型任务,我通常使用这个成语:

 private final int NUM_THREADS = Runtime.getRuntime().availableProcessors() + 1; private final ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS); 

正如其他人所指出的,使用更multithreading只会引入不必要的上下文切换。

显然,如果任务执行某些I / O和其他阻塞操作,则这不适用,并且更大的池将有意义。

编辑

要回复@MartinJames评论,我已经运行了一个(简单化)基准测试 – 结果显示,从池大小=处理器数量+ 1到100只会略微降低性能(让我们称之为5%) – 更高的数字( 1000和10000)确实显着地达到了性能。

结果是10次运行的平均值:
泳池大小:9:238毫秒。 //(NUM_CORES + 1)
泳池大小:100:245毫秒。
泳池大小:1000:319毫秒。
泳池大小:10000:2482毫秒。

码:

 public class Test { private final static int NUM_CORES = Runtime.getRuntime().availableProcessors(); private static long count; private static Runnable r = new Runnable() { @Override public void run() { int count = 0; for (int i = 0; i < 100_000; i++) { count += i; } Test.count += count; } }; public static void main(String[] args) throws Exception { //warmup runWith(10); //test runWith(NUM_CORES + 1); runWith(100); runWith(1000); runWith(10000); } private static void runWith(int poolSize) throws InterruptedException { long average = 0; for (int run = 0; run < 10; run++) { //run 10 times and take the average Test.count = 0; ExecutorService executor = Executors.newFixedThreadPool(poolSize); long start = System.nanoTime(); for (int i = 0; i < 50000; i++) { executor.submit(r); } executor.shutdown(); executor.awaitTermination(10, TimeUnit.SECONDS); long end = System.nanoTime(); average += ((end - start) / 1000000); System.gc(); } System.out.println("Pool size: " + poolSize + ": " + average / 10 + " ms. "); } } 

“变聪明并管理工作量以保持CPU低于100%会让我更快吗?”

可能不是。

正如其他人发布的那样,如果大多数任务都是CPU密集型的话,那么线程池就有100个线程太多了。 它对典型系统的性能没有太大影响 – 如果过多则会导致4个线程坏,400个坏。

你是如何决定100个线程的? 为什么不16,说?

‘线程数量不大,比如高达100’ – 它有变化吗? 只需在启动时创建16并停止管理它们 – 只需将队列传递给它们并忘记它们。

可怕的想法 – 你没有为每项任务创建一个新线程,是吗?

您应该保持100%的使用率,但线程数最少。 100个线程看起来太多了。