`

如何计算一个合适的线程池大小参数

    博客分类:
  • Java
阅读更多
原文在这里

下面是一个计算的框架代码:
/**
 * A class that calculates the optimal thread pool boundaries. It takes the desired target utilization and the desired
 * work queue memory consumption as input and retuns thread count and work queue capacity.
 * 
 * @author Niklas Schlimm
 * 
 */
public abstract class PoolSizeCalculator {

 /**
  * The sample queue size to calculate the size of a single {@link Runnable} element.
  */
 private final int SAMPLE_QUEUE_SIZE = 1000;

 /**
  * Accuracy of test run. It must finish within 20ms of the testTime otherwise we retry the test. This could be
  * configurable.
  */
 private final int EPSYLON = 20;

 /**
  * Control variable for the CPU time investigation.
  */
 private volatile boolean expired;

 /**
  * Time (millis) of the test run in the CPU time calculation.
  */
 private final long testtime = 3000;

 /**
  * Calculates the boundaries of a thread pool for a given {@link Runnable}.
  * 
  * @param targetUtilization
  *            the desired utilization of the CPUs (0 <= targetUtilization <= 1)
  * @param targetQueueSizeBytes
  *            the desired maximum work queue size of the thread pool (bytes)
  */
 protected void calculateBoundaries(BigDecimal targetUtilization, BigDecimal targetQueueSizeBytes) {
  calculateOptimalCapacity(targetQueueSizeBytes);
  Runnable task = creatTask();
  start(task);
  start(task); // warm up phase
  long cputime = getCurrentThreadCPUTime();
  start(task); // test intervall
  cputime = getCurrentThreadCPUTime() - cputime;
  long waittime = (testtime * 1000000) - cputime;
  calculateOptimalThreadCount(cputime, waittime, targetUtilization);
 }

 private void calculateOptimalCapacity(BigDecimal targetQueueSizeBytes) {
  long mem = calculateMemoryUsage();
  BigDecimal queueCapacity = targetQueueSizeBytes.divide(new BigDecimal(mem), RoundingMode.HALF_UP);
  System.out.println("Target queue memory usage (bytes): " + targetQueueSizeBytes);
  System.out.println("createTask() produced " + creatTask().getClass().getName() + " which took " + mem
    + " bytes in a queue");
  System.out.println("Formula: " + targetQueueSizeBytes + " / " + mem);
  System.out.println("* Recommended queue capacity (bytes): " + queueCapacity);
 }

 /**
  * Brian Goetz' optimal thread count formula, see 'Java Concurrency in Practice' (chapter 8.2)
  * 
  * @param cpu
  *            cpu time consumed by considered task
  * @param wait
  *            wait time of considered task
  * @param targetUtilization
  *            target utilization of the system
  */
 private void calculateOptimalThreadCount(long cpu, long wait, BigDecimal targetUtilization) {
  BigDecimal waitTime = new BigDecimal(wait);
  BigDecimal computeTime = new BigDecimal(cpu);
  BigDecimal numberOfCPU = new BigDecimal(Runtime.getRuntime().availableProcessors());
  BigDecimal optimalthreadcount = numberOfCPU.multiply(targetUtilization).multiply(
    new BigDecimal(1).add(waitTime.divide(computeTime, RoundingMode.HALF_UP)));
  System.out.println("Number of CPU: " + numberOfCPU);
  System.out.println("Target utilization: " + targetUtilization);
  System.out.println("Elapsed time (nanos): " + (testtime * 1000000));
  System.out.println("Compute time (nanos): " + cpu);
  System.out.println("Wait time (nanos): " + wait);
  System.out.println("Formula: " + numberOfCPU + " * " + targetUtilization + " * (1 + " + waitTime + " / "
    + computeTime + ")");
  System.out.println("* Optimal thread count: " + optimalthreadcount);
 }

 /**
  * Runs the {@link Runnable} over a period defined in {@link #testtime}. Based on Heinz Kabbutz' ideas
  * (http://www.javaspecialists.eu/archive/Issue124.html).
  * 
  * @param task
  *            the runnable under investigation
  */
 public void start(Runnable task) {
  long start = 0;
  int runs = 0;
  do {
   if (++runs > 5) {
    throw new IllegalStateException("Test not accurate");
   }
   expired = false;
   start = System.currentTimeMillis();
   Timer timer = new Timer();
   timer.schedule(new TimerTask() {
    public void run() {
     expired = true;
    }
   }, testtime);
   while (!expired) {
    task.run();
   }
   start = System.currentTimeMillis() - start;
   timer.cancel();
  } while (Math.abs(start - testtime) > EPSYLON);
  collectGarbage(3);
 }

 private void collectGarbage(int times) {
  for (int i = 0; i < times; i++) {
   System.gc();
   try {
    Thread.sleep(10);
   } catch (InterruptedException e) {
    Thread.currentThread().interrupt();
    break;
   }
  }
 }

 /**
  * Calculates the memory usage of a single element in a work queue. Based on Heinz Kabbutz' ideas
  * (http://www.javaspecialists.eu/archive/Issue029.html).
  * 
  * @return memory usage of a single {@link Runnable} element in the thread pools work queue
  */
 public long calculateMemoryUsage() {
  BlockingQueue<Runnable> queue = createWorkQueue();
  for (int i = 0; i < SAMPLE_QUEUE_SIZE; i++) {
   queue.add(creatTask());
  }
  long mem0 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
  long mem1 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
  queue = null;
  collectGarbage(15);
  mem0 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
  queue = createWorkQueue();
  for (int i = 0; i < SAMPLE_QUEUE_SIZE; i++) {
   queue.add(creatTask());
  }
  collectGarbage(15);
  mem1 = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
  return (mem1 - mem0) / SAMPLE_QUEUE_SIZE;
 }

 /**
  * Create your runnable task here.
  * 
  * @return an instance of your runnable task under investigation
  */
 protected abstract Runnable creatTask();

 /**
  * Return an instance of the queue used in the thread pool.
  * 
  * @return queue instance
  */
 protected abstract BlockingQueue<Runnable> createWorkQueue();

 /**
  * Calculate current cpu time. Various frameworks may be used here, depending on the operating system in use. (e.g.
  * http://www.hyperic.com/products/sigar). The more accurate the CPU time measurement, the more accurate the results
  * for thread count boundaries.
  * 
  * @return current cpu time of current thread
  */
 protected abstract long getCurrentThreadCPUTime();

}


下面是一个具体的计算场景:
public class MyPoolSizeCalculator extends PoolSizeCalculator {

 public static void main(String[] args) throws InterruptedException, 
                                               InstantiationException, 
                                               IllegalAccessException,
                                               ClassNotFoundException {
  MyThreadSizeCalculator calculator = new MyThreadSizeCalculator();
  calculator.calculateBoundaries(new BigDecimal(1.0), 
                                 new BigDecimal(100000));
 }

 protected long getCurrentThreadCPUTime() {
  return ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime();
 }

 protected Runnable creatTask() {
  return new AsynchronousTask(0, "IO", 1000000);
 }
 
 protected BlockingQueue<runnable> createWorkQueue() {
  return new LinkedBlockingQueue<>();
 }

}


运行得到的计算结果:
引用
Target queue memory usage (bytes): 100000 
createTask() produced com.schlimm.java7.nio.threadpools.AsynchronousTask which took 40 bytes in a queue 
Formula: 100000 / 40 
* Recommended queue capacity (bytes): 2500 
Number of CPU: 2 
Target utilization: 1.0 
Elapsed time (nanos): 3000000000 
Compute time (nanos): 906250000 
Wait time (nanos): 2093750000 
Formula: 2 * 1.0 * (1 + 2093750000 / 906250000) 
* Optimal thread count: 6.0 


最后的一个推荐设置:
ThreadPoolExecutor pool = 
       new ThreadPoolExecutor(6, 6, 
                              0L, TimeUnit.MILLISECONDS, 
                              new LinkedBlockingQueue<Runnable>(2500));
pool.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
分享到:
评论

相关推荐

    线程池详解:线程池七大核心参数、线程池工作原理、线程池的创建方式、线程池的拒绝策略、如何合理分配线程池大小

    1. 当提交一个新任务时,如果当前线程池中的线程数量少于corePoolSize,会直接创建新线程来执行任务。 2. 如果线程池已达到corePoolSize,但任务队列未满,新任务会放入任务队列中等待。 3. 当线程池中的线程数大于...

    一个线程池封装类及例子程序

    1. **线程池大小**:线程池中线程的最大数量,可以通过参数进行配置。 2. **工作队列**:用于存放待处理任务的队列,当所有线程都在忙碌时,新任务会被放入队列等待。 3. **线程管理策略**:当工作队列满时,线程池...

    VC写的一个线程池的管理实例

    - **线程池管理器**:控制线程池的生命周期,如添加/删除线程,设置线程池大小等。 4. **线程池的优势**: - **性能优化**:避免了线程创建和销毁的开销,减少上下文切换次数。 - **资源管理**:动态调整线程...

    一个简单的线程池例子

    线程池是多线程编程中的一个重要概念,它在Windows操作系统以及其他支持多线程操作系统的环境中广泛应用。线程池是一种管理线程资源的有效方式,通过预先创建并维护一定数量的线程来处理任务,而不是每次需要执行新...

    linux 实现一个简单的线程池及工作

    本实例将深入探讨如何在Linux下实现一个简单的线程池,并介绍相关的关键知识点。 1. **线程与线程池的概念** - **线程**:是操作系统分配CPU时间片的基本单位,是程序执行的流,一个进程中可以包含多个线程,它们...

    Java线程池使用说明

    可以根据系统承载能力动态调整线程池大小,避免服务器资源过度消耗。 在Java中,与线程池相关的几个重要类和接口包括: 1. Executor:它是Java中线程池的顶级接口,定义了执行线程的抽象方法,但它本身并不直接...

    【Java分享客栈】SpringBoot线程池参数搜一堆资料还是不会配,我花一天测试换你此生明白。.doc

    在SpringBoot应用程序中,线程池参数的配置是一个非常重要的环节,直接影响着应用程序的性能和稳定性。然而,许多开发者对线程池参数的配置感到困惑和不了解,导致线上环境出现问题。本文将通过一个简单的案例,详细...

    线程池技术在网络游戏服务器中的应用

    线程池技术的设计和实现需要考虑多个因素,例如线程池的大小、线程池的类型和线程池的管理方式。在设计和实现线程池技术时,需要选择合适的线程池模型和参数,以确保线程池的稳定运行和高效运算。 小结 ...

    一个完整的线程池的实例

    2. **`Worker` 类**:这是一个内部类,继承自`Thread`,负责实际的任务执行。 - `runner`:当前`Worker`正在执行的`Runnable`任务。 - `wakeup` 方法:用于唤醒当前处于等待状态的`Worker`,使其开始执行新的任务...

    线程池代码

    线程池是多线程编程中的一个重要概念,它是一种线程使用模式,通过预先创建一定数量的线程来处理任务,而不是每当有新任务提交时就创建新的线程。线程池的使用可以有效地减少系统资源的消耗,提高系统的响应速度和...

    简单线程池与线程池检查的实现

    3. 执行任务:工作线程从队列中取出任务,执行完毕后返回工作队列等待下一个任务。 4. 线程管理:根据系统负载和设定策略,动态调整线程池的大小。 5. 销毁线程池:当线程池不再需要时,可以安全地关闭线程池,释放...

    C++ 线程池设计

    任务参数通常封装在一个结构体中,包含输入参数和返回值。 4. **线程状态管理**:线程的状态由一个布尔标志(如`is_busy`)表示,用于跟踪线程是否正在处理任务。此外,使用条件变量(`pthread_cond_t`)和互斥锁...

    java线程池概念.txt

     也就是说corePoolSize就是线程池大小,maximumPoolSize在我看来是线程池的一种补救措施,即任务量突然过大时的一种补救措施。  不过为了方便理解,在本文后面还是将corePoolSize翻译成核心池大小。  ...

    线程池java

    2. **任务调度**:线程池内部维护了一个或多个工作线程,它们会从任务队列中取出任务并执行。当工作线程完成任务后,会返回到等待状态或者被销毁(取决于当前线程池的状态)。 3. **线程管理**:线程池会根据当前的...

    JAVA线程池原理以及几种线程池类型介绍

    1. **FixedThreadPool**:创建一个固定大小的线程池,线程数量由用户指定。当所有线程都在执行任务时,新提交的任务会被放入队列中等待执行。 2. **CachedThreadPool**:创建一个可根据需要创建新线程的线程池,但在...

    java 四种线程池实例

    2. **FixedThreadPool**: `Executors.newFixedThreadPool(nThreads)`创建一个固定大小的线程池,它维护的线程数量始终保持不变。当一个新任务提交而所有线程都在工作时,任务会被添加到队列中等待。这种线程池适合...

    线程池 threadpool

    因此,合理设定线程池大小是优化并发性能的关键。 5. **工作线程与IO完成端口(IOCP)**:在Windows下,结合IO完成端口可以进一步提高线程池的效率,特别是处理大量I/O操作时。IOCP允许一个线程处理多个已完成的I/O...

    MySQL的线程池原理学习教程

    MySQL的线程池原理是MySQL ...正确配置线程池大小、合理设置调度策略,可以有效提高数据库服务的稳定性和响应速度。在实际应用中,应根据服务器的硬件资源、并发量和业务特性来调整线程池的相关参数,以达到最佳性能。

    JAVA集中常用的线程池比较.pdf

    其次,资源不足是一个关键问题,不适当的线程池大小可能导致内存和CPU资源的过度消耗。线程本身就需要内存和堆栈空间,而且可能还需要额外的系统资源,如本机线程。过多的线程切换也会带来性能损失。此外,如果任务...

    线程池的简单实现

    线程池是多线程编程中的一个重要概念,它在服务器端程序中被广泛使用,以提高资源利用率和系统性能。线程池通过预先创建并管理一定数量的线程,可以有效地减少线程创建和销毁的开销,同时提供了一种灵活的线程调度和...

Global site tag (gtag.js) - Google Analytics