根据 CPU 和 RAM 使用情况调整线程池的 Java Executor

Posted

技术标签:

【中文标题】根据 CPU 和 RAM 使用情况调整线程池的 Java Executor【英文标题】:Java Executor that adjusts thread pool based on CPU and RAM usage 【发布时间】:2014-09-14 22:06:57 【问题描述】:

我的应用程序使用 Executor 为大量任务提供线程池。我通过分析和基准测试确定,当每个内核有多个线程时,我的应用程序运行速度最快。一个好的启发式方法是从每个内核 4 个线程开始,直到达到 >90% CPU 或 >90% RAM。

是否有可用的执行器可以立即执行此操作?是自动为每个内核使用 N 个线程(而不仅仅是一个),还是在理想情况下根据 CPU 和 RAM 使用情况来限制线程池的大小?

失败了 - 如何以编程方式确定内核数量?

【问题讨论】:

【参考方案1】:

一种方法是使用ThreadPoolExecutor,core size 为 1,起始maximum pool size 为 4,然后根据内存和 CPU 使用情况动态调整最大池大小。

恕我直言,更大的问题是如何测量内存使用情况和 CPU 负载。内存使用很简单:

public double memUsageRatio() 
  Runtime r = Runtime.getRuntime();
  return (double) (r.totalMemory() - r.freeMemory()) / r.maxMemory();

对于 CPU 负载,问题可能更大,具体取决于您运行的平台。在 Linux 上,您可以使用:

ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();

这将返回最后一分钟的系统负载平均值。不幸的是,在 Windows 上,此方法总是返回 -1。过去,我通过计算所有线程的 CPU 时间总和除以所有处理器的所有经过时间的总和,将其替换为给定时间间隔内系统负载平均值的近似值。这只是一个近似值,但在大多数情况下效果都很好:

import java.lang.management.*;

public class CPUUsageCollector implements Runnable 
  private final static long INTERVAL = 1000L; // polling interval in ms
  private long totalCpuTime = 0L; // total CPU time in millis
  private double load = 0d; // average load over the interval
  ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
  boolean stopped = false;

  @Override
  public void run() 
    try 
      while (!isStopped()) 
        long start = System.currentTimeMillis();
        long[] ids = threadMXBean.getAllThreadIds();
        long time = 0L;
        for (long id: ids) 
          long l = threadMXBean.getThreadCpuTime(id);
          if (l >= 0L) time += l;
        
        long newCpuTime = time / 1000000L;
        synchronized(this) 
          long oldCpuTime = totalCpuTime;
          totalCpuTime = newCpuTime;
          // load = CPU time difference / sum of elapsed time for all CPUs
          load = (double) (newCpuTime - oldCpuTime) / 
           (double) (INTERVAL * Runtime.getRuntime().availableProcessors());
        
        long sleepTime = INTERVAL - (System.currentTimeMillis() - start);
        goToSleep(sleepTime <= 0L ? INTERVAL : sleepTime);
      
     catch (Exception e) 
      e.printStackTrace();
    
  

  public synchronized double getLoad() 
    return load;
  

  public synchronized void goToSleep(final long time) 
    try 
      wait(time);
     catch(InterruptedException e) 
      e.printStackTrace();
    
  

  public synchronized boolean isStopped() 
    return stopped;
  

  public synchronized void setStopped(final boolean stopped) 
    this.stopped = stopped;
  

【讨论】:

【参考方案2】:
Runtime.availableProcessors()

Javadoc:

返回 Java 虚拟机可用的处理器数量。 此值可能会在虚拟机的特定调用期间发生变化。因此,对可用处理器数量敏感的应用程序应偶尔轮询此属性并适当调整其资源使用情况。

【讨论】:

以上是关于根据 CPU 和 RAM 使用情况调整线程池的 Java Executor的主要内容,如果未能解决你的问题,请参考以下文章

java四种线程池简介,使用

.net 中线程的 RAM 和 CPU 限制

线程池讲解

Java核心深入理解线程池ThreadPool

多线程--线程池的正确打开方式

MySQL参数优化根据架构优化