`

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

    博客分类:
  • 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());
分享到:
评论

相关推荐

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

    线程池详解:线程池七大核心参数、线程池工作原理、线程池的创建方式、线程池的拒绝策略、如何合理分配线程池大小 文件内容包括pdf文件和思维导图

    java线程池概念.txt

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

    线程池与对象池的融合

    这是一个很简单的实现啦对象池与线程池的融合,方法的主要参数,线程池大小、对象池大小、对象的创建工厂(继承接口自己实现),执行时传入方面名称即可。(invoke方法第一个为参数为方法名,第二是可变参数(及方法...

    WAS服务器参数配置

    websphere参数必须要结合运行环境的实际情况来调整,例如Web Container的线程池大小、数据源连接池大小、语句高速缓存大小(Prepared statement cache size),这几项参数都很重要,要结合实际的并发量和服务器的资源...

    更好的使用Java线程池

    这篇文章结合Doug Lea大神在JDK1.5提供的JCU包,分别从线程池大小参数的设置、工作线程的创建、空闲线程的回收、阻塞队列的使用、任务拒绝策略、线程池Hook等方面来了解线程池的使用,其中涉及到一些细节包括不同...

    WebSphere参数调优.txt

    在EJB1.1规范中,要求远程方法一律使用参数值传递方式来调用,如果调用EJB的Servlet或者其它EJB是部署在同一个应用服务器下,那么它们是共享一个JVM的,也就是说可以使得函数调用的方式变为参数引用传递,这样的话,...

    java大批量导入excel,多线程加分片处理的dome

    importExcel 方法接收一个 Excel 文件对象、批大小 batchSize 和线程数 threadCount 作为参数。首先,使用 ExcelReader 对象读取 Excel 文件,并计算出总行数和分片大小和数量;然后,创建一个固定数量的线程池,...

    python开发的Web爬虫

    使用python编写一个网站爬虫程序,支持参数如下: spider.py -u url -d deep -f logfile -l loglevel(1-5) --testself --thread number --dbfile filepath --key=”HTML5” 参数说明: -u 指定爬虫开始地址 -d 指定...

    鱼刺类_线程池Ex的命令详解及框架构建-易语言

    上图中:投递任务()命令 传递了两个参数 一个是局_计次 一个是 0, 投递 局_计次 可以在任务函数中获取到 用处也比较大,比如可以用作超级列表框的索引。(前提是已经插入了) 等待任务动态().为什么要等待呢,又是时候...

    10个线程的Python多线程爬虫(采集新浪数据).rar

     --thread 指定线程池大小,多线程爬取页面,可选参数,默认10  --dbfile 存放结果数据到指定的数据库(sqlite)文件中  --key 页面内的关键词,获取满足该关键词的网页,可选参数,默认为所有页面  -l 日志记录...

    ThreadPoolUtil.java

    该工具类里面有三种默认的创建方式,只需要传入核心线程池大小即可,三种默认的分别为满了之后丢弃抛异常,满了之后丢弃不抛异常,满了之后丢弃最前面的重试插入 还有一种自定义的创建方式,高度自定义化,完全自己...

    第7章-JUC多线程v1.1.pdf

    CorePoolSize: 核心线程池大小, 如果核心线程池有空闲的位置, 新的任务就会被核心线程池新建一个线程执行, 执行完毕不会销毁线程, 线程会进入缓冲队列等待再次被运行 MaximunPoolSize: 线程池能创建最大的线程数量,...

    Python多线程爬虫

    使用python编写一个网站爬虫程序,支持参数如下: spider.py -u url -d deep -f logfile -l loglevel(1-5) --testself -thread number --dbfile filepath --key=”HTML5” 参数说明: -u 指定爬虫开始地址 -d 指定...

    [鱼刺多线程-鱼刺类_多线程应用模块v5.4完整源码

    资源介绍:。鱼刺类_多线程应用 - 更新日志。...*修正了参数 实现了线程、线程池创建时可以调整初始栈大小来突破单进程1500线程数限制。*部分控件移除了汇编加减法的调用(效率存在问题)。5.2.0(2016-

    开源C# SOCKET服务器,支持WINDOWS云,LIUNX MONO 2.0

    SuperSocket 是一个轻量级的,可扩展的,跨平台的.NET Socket 服务器开发框架,可用来构建一个基于命令的服务器端Socket 程序,而无需了解如何使用Socket,如何维护Socket连接,亦无需了解Socket是如何工作的。...

    swing变形窗口

    参考了csdn的一篇文章使用透明图片作为swing窗口的形状并把图片设为背景,由于图片有点大...压缩包中解压后jar文件可以直接运行,也可以把jar解压缩出来,改变ImageUtil类中getShape方法的size参数可以调整线程池的大小

    一个开源的Java基础工具包

    此工具我不再更新,里面... 若想自定义线程池大小或独立控制,可调用newExecutor()实例化一个线程池 excAsync()执行一个异步方法 3、com.baijob.commonTools.thread.SyncQueue 阻塞队列,简化了JDK的BlockingQueue

    CLR.via.C#.(中文第3版)(自制详细书签)Part2

    3.4 在生成的程序集中引用一个强命名程序集 3.5 强命名程序集能防范篡改 3.6 延迟签名 3.7 私有部署强命名程序集 3.8 “运行时”如何解析类型引用 3.9 高级管理控制(配置) 3.9.1 发布者策略控制 第4章 类 型...

    app:轻量级java应用框架模板

    基于系统只一个公共线程池:所有的执行都被抽象成Runnable加入到公共线程池中执行所以系统性能只由线程池大小属性sys.exec.corePoolSize = 4和jvm内存参数-Xmx512m控制框架由一个AppContext容器装载用到的所有服务类...

    java微信公众号MVC开发框架

    jwx是开源的java公众号开发MVC框架,基于spring配置文件和微信消息或事件注解,通过微信上下文处理一个或多个微信公众号服务请求。目的主要有两个,其一生封装微信请求xml消息为java实体对象,将返回对象转换为xml...

Global site tag (gtag.js) - Google Analytics