http://ifeve.com/how-to-calculate-threadpool-size/

背景

在我们日常业务开发过程中,或多或少都会用到并发的功能。那么在用到并发功能的过程中,就肯定会碰到下面这个问题

并发线程池到底设置多大呢?

通常有点年纪的程序员或许都听说这样一个说法 (其中 N 代表 CPU 的个数)

  1. CPU 密集型应用,线程池大小设置为 N + 1

  2. IO 密集型应用,线程池大小设置为 2N

这个说法到底是不是正确的呢?

其实这是极不正确的。那为什么呢?

  • 首先我们从反面来看,假设这个说法是成立的,那我们在一台服务器上部署多少个服务都无所谓了。因为线程池的大小只能服务器的核数有关,所以这个说法是不正确的。那具体应该怎么设置大小呢?

  • 假设这个应用是两者混合型的,其中任务即有 CPU 密集,也有 IO 密集型的,那么我们改怎么设置呢?是不是只能抛硬盘来决定呢?

那么我们到底该怎么设置线程池大小呢?有没有一些具体实践方法来指导大家落地呢?让我们来深入地了解一下。

Little's Law(利特尔法则)

一个系统请求数等于请求的到达率与平均每个单独请求花费的时间之乘积

假设服务器单核的,对应业务需要保证请求量(QPS):10 ,真正处理一个请求需要 1 秒,那么服务器每个时刻都有 10 个请求在处理,即需要 10 个线程

同样,我们可以使用利特尔法则(Little’s law)来判定线程池大小。我们只需计算请求到达率和请求处理的平均时间。然后,将上述值放到利特尔法则(Little’s law)就可以算出系统平均请求数。估算公式如下

*线程池大小 = ((线程 IO time + 线程 CPU time )/线程 CPU time ) CPU数目**

具体实践

通过公式,我们了解到需要 3 个具体数值

  1. 一个请求所消耗的时间 (线程 IO time + 线程 CPU time)

  2. 该请求计算时间 (线程 CPU time)

  3. CPU 数目

请求消耗时间

Web 服务容器中,可以通过 Filter 来拦截获取该请求前后消耗的时间

public class MoniterFilter implements Filter {  private static final Logger logger = LoggerFactory.getLogger(MoniterFilter.class); @Override  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,   ServletException {  long start = System.currentTimeMillis();   HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletResponse httpResponse = (HttpServletResponse) response; String uri = httpRequest.getRequestURI();  String params = getQueryString(httpRequest);   try {   chain.doFilter(httpRequest, httpResponse);  } finally { long cost = System.currentTimeMillis() - start;    logger.info("access url [{}{}], cost time [{}] ms )", uri, params, cost); }   private String getQueryString(HttpServletRequest req) { StringBuilder buffer = new StringBuilder("?");   Enumeration<String> emParams = req.getParameterNames();  try {   while (emParams.hasMoreElements()) {    String sParam = emParams.nextElement();    String sValues = req.getParameter(sParam); buffer.append(sParam).append("=").append(sValues).append("&"); }   return buffer.substring(0, buffer.length() - 1);    } catch (Exception e) { logger.error("get post arguments error", buffer.toString());  }   return "";    }
}

CPU 计算时间

CPU 计算时间 = 请求总耗时 - CPU IO time

假设该请求有一个查询 DB 的操作,只要知道这个查询 DB 的耗时(CPU IO time),计算的时间不就出来了嘛,我们看一下怎么才能简洁,明了的记录 DB 查询的耗时。通过(JDK 动态代理/ CGLIB)的方式添加 AOP 切面,来获取线程 IO 耗时。代码如下,请参考

public class DaoInterceptor implements MethodInterceptor { private static final Logger logger = LoggerFactory.getLogger(DaoInterceptor.class);    @Override  public Object invoke(MethodInvocation invocation) throws Throwable {    StopWatch watch = new StopWatch(); watch.start();  Object result = null;  Throwable t = null;    try {   result = invocation.proceed(); } catch (Throwable e) { t = e == null ? null : e.getCause(); throw e;    } finally { watch.stop();   logger.info("({}ms)", watch.getTotalTimeMillis());    }   return result;  }   }

CPU 数目

逻辑 CPU 个数 ,设置线程池大小的时候参考的 CPU 个数

cat /proc/cpuinfo| grep "processor"| wc -l

总结

合适的配置线程池大小其实很不容易,但是通过上述的公式和具体代码,我们就能快速、落地的算出这个线程池该设置的多大。不过最后的最后,我们还是需要通过压力测试来进行微调,只有经过压测测试的检验,我们才能最终保证的配置大小是准确的。

长按订阅更多精彩▼

如有收获,点个在看,诚挚感谢

如何确定线程池的大小?相关推荐

  1. java 线程池 初始大小,Java线程池ThreadPoolExecutor的实现和参数

    接文章Java8线程池--底层为LinkedBlockingQueue的ThreadPoolExecutor,文章中简单介绍了线程池保持线程,并且从阻塞队列中获取任务执行的流程.本篇文章详细介绍线程池 ...

  2. 如何合理确定线程池的大小

    在java中,几乎所有需要异步或者并发执行任务的程序都可以使用线程池.在开发过程中,合理的使用线程池能够带来3个好处 首先是降低资源消耗.通过重复利用已创建的线程降低创建线程和销毁线程所带来的开销. ...

  3. 合理的估算线程池的大小

    一般说来,大家认为线程池的大小经验值应该这样设置:(其中N为CPU的个数) 如果是CPU密集型应用,则线程池大小设置为N+1 如果是IO密集型应用,则线程池大小设置为2N+1 如果一台服务器上只部署这 ...

  4. 从原理上搞懂如何设置线程池参数大小?

    我们在使用线程池的时候,会有两个疑问点: 线程池的线程数量设置过多会导致线程竞争激烈 如果线程数量设置过少的话,还会导致系统无法充分利用计算机资源 那么如何设置才不会影响系统性能呢? 其实线程池的设置 ...

  5. java 线程池 初始大小_为什么tomcat的默认线程池大小如此之大? - java

    我注意到默认的tomcat 7线程池大小似乎是200. 但是普通的CPU似乎有16个内核. 因此只能并行执行16个线程 为什么tomcat使用那么多线程. 参考方案 多年以来,许多单核计算机问世,并且 ...

  6. 线程池系列三:动态修改线程池队列大小

    线程池中的队列要求的是阻塞队列,作用主要是当线程池处理任务能力不足时,队列存储多余的任务,从而起到削峰和缓冲的目的. 可以选择的队列种类很多,如何选择合适的队列应用到自己的线程池中?就需要了解他们的优 ...

  7. Java线程池的使用(合理设置线程池的大小)

    线程池工具类 import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; im ...

  8. java线程池队列大小_GitHub - sunshanpeng/dark_magic: 合理估算线程池大小及队列数

    合理估算java的线程池大小及队列数 原理分析 先来一个天真的估算方法:假设要求一个系统的TPS(Transaction Per Second或者Task Per Second)至少为20,然后假设每 ...

  9. java 线程池 固定大小_使用Executors服务在Java中创建固定大小线程池的最佳方法...

    查看源代码,您将意识到: Executors.newFixedThreadPool(threadPoolSize); 相当于: return new ThreadPoolExecutor(thread ...

最新文章

  1. 学生管理系统(用maven来导入jar包)
  2. 为什么 IPv6 难以取代 IPv4
  3. Vue:触发视图更新的hack
  4. Laravel日志查看器 -- log-viewer扩展
  5. html之文档的头部和元数据定义(下,未写完)
  6. 如何为 SQL Server 2000 分发和安装 SQL-DMO(引用)
  7. 基于JAVA+SpringMVC+Mybatis+MYSQL的员工事物管理系统
  8. codevs1197 Vigenère密码
  9. mac 安装jdk_Mac安装rJava天坑
  10. mysql游标触发器批量_MySQL游标和触发器
  11. SQL Server 存储(5/8):理解IAM 页
  12. c#使用word、excel、pdf ——转
  13. 操作系统原理与Linux实践教程申丰山版习题2的2-3答案
  14. Java面试题(140多道高频面试题2022版)
  15. 猜数字游戏:随机生成一个1-100之间的数据,提示用户猜测,猜大提示过大,猜小提示过小,直到猜中结束游戏
  16. 2022年最新CPU天梯图 台式机cpu天梯图2022
  17. Stylus入门教程--实例(1)
  18. 麻雀要革命2 第31节:不共戴天之仇
  19. EF 配置Oracle数据库 EF ORA-01918: 用户 'dbo' 不存在
  20. r软件 linux,分享|Linux 上好用的 R 语言 IDE

热门文章

  1. HDU2121(最小树形图的模版算法题)
  2. 蒟蒻的第一篇博客CF1041C Coffee Break(二分+贪心+set)
  3. android路由器 设备数,手机查看wifi连接人数_手机查看wifi连接设备数量-192路由网...
  4. UVA1626 括号序列 Brackets sequence(区间DP匹配括号,输出匹配方案)
  5. python中6 2是什么意思_python2.6中SyntaxError是什么错误?
  6. 原生js实现点击按钮切换全屏!
  7. OpenCV-Python,计算机视觉开发利器
  8. Jenkins 持续集成 概念(学习笔记二十六)
  9. Redis的多种启动方式比较!
  10. 网络系统管理之静态路由配置