当前位置: 首页 > 科技观察

你究竟如何设置Java线程池的大小?

时间:2023-03-14 23:33:26 科技观察

在我们日常的业务开发过程中,或多或少都会用到并发函数。那么在使用并发功能的过程中,肯定会遇到下面的问题。并发线程池有多大?一般老一点的程序员可能听过这样的说法(其中N代表CPU个数)CPU密集型应用,线程池大小设置为N+1IO密集型应用,线程池大小设置为2N。对不对?其实,这是极不正确的。所以为什么?首先,让我们从消极的一面来看。假设这个说法是正确的,那么我们在一台服务器上部署多少服务并不重要。因为线程池的大小只能和服务器的核数有关,所以这种说法是不正确的。那么size应该怎么设置呢?假设这个应用程序是两者的混合体,任务既是CPU密集型又是IO密集型,我们应该如何设置它们?难道只是扔硬盘来决定?那么我们如何设置线程池大小呢?有没有具体的实用方法可以指导大家去实施呢?让我们深入了解一下。利特尔定律(Little'sLaw)系统请求的数量等于请求到达率与每个单独请求所花费的平均时间的乘积。假设服务器是单核的,对应的业务需要保证请求数(QPS):10,实际处理一个请求如果需要1秒,那么服务器每时刻有10个请求在处理,即,需要10个线程。同样,我们可以使用利特尔定律来确定线程池的大小。我们只需要计算请求到达率和请求处理的平均时间。然后,将以上数值带入利特尔定律,计算平均系统请求数。估算公式如下*线程池大小=((线程IO时间+线程CPU时间)/线程CPU时间)CPU个数**具体实践通过公式我们了解到时间需要3个具体值一个请求消耗的时间(线程IO时间+线程CPU时间)请求的计算时间(线程CPU时间)CPU个数请求消耗的时间在Web服务容器中,可以通过拦截请求前后的耗时FilterpublicclassMoniterFilterimplementsFilter{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(MoniterFilter.class);@OverridepublicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{longstart=System.currentTimeMillis();HttpServletRequesthttpRequest=(HttpServletRequest)request;HttpServletResponsehttpResponse=(HttpServletResponse)response;Stringuri=httpRequest.getRequestURI();Stringparams=getQueryString(httpRequest);试试{chain.doFilter(httpRequest,httpResponse);}finally{longcost=System.currentTimeMillis()-start;logger.info("accessurl[{}{}],costtime[{}]ms)",uri,params,cost);}privateStringgetQueryString(HttpServletRequestreq){StringBuilderbuffer=newStringBuilder("?");EnumerationemParams=req.getParameterNames();try{while(emParams.hasMoreElements()){StringsParam=emParams.nextElement();StringsValues=req.getParameter(sParam);buffer.append(sParam).append("=").append(sValues).append("&");}returnbuffer.substring(0,buffer.length()-1);}catch(Exceptione){logger.error("getpostargumentserror",buffer.toString());}return"";}}CPU计算时间CPU计算时间=总请求时间-CPUIO时间假设请求有查询DB操作,只要知道这个查询DB的耗时即可(CPUIOtime),计算时间就出来了,我们来看看如何通过加入AOP切面(JDK动态代理/CGLIB)获取线程IO耗时,来简洁明了的记录DB查询的耗时。代码如下,请参考:invocation.proceed();}catch(Throwablee){t=e==null?null:e.getCause();throwe;}finally{watch.stop();logger.info("({}ms)",watch.getTotalTimeMillis());}returnresult;}}CPUnumberLogicalCPUnumber,设置线程池大小时引用的CPU数cat/proc/cpuinfo|grep"processor"|wc-l总结合适的配置线程池的size其实并不容易,但是通过上面的公式和具体的代码,我们可以快速有效的计算出线程池的大小。但最终还是需要通过压力测试进行微调。只有经过压测测试,才能最终保证配置大小是准确的。