关于线程池ThreadPoolExecutor参数设置那些事

       由于近期工作需要,最近需要测试开发某组件(该组件中用到了ThreadPoolExecutor)的性能,测试工具是soapUI,由于以前对测试性能方面接触较少。所以借此机会再网上查阅了相关资料,总结如下。

     线程数到底和什么有关系?

        其中一种说法,是线程数和cpu数有关系。

              总体意思是,当应用是IO密集型时,线程数T=2N+1.          

                                    当应用是CPU密集型时,  线程数T=N+1

              T=线程数   N=cpu逻辑盒数

             IO密集型应用:系统的CPU性能相对硬盘、内存要好很多,大部分的情况是CPU在等I/O (硬盘/内存) 的读/写操作,此时                                            CPU Loading并不高。

             CPU密集型应用:系统的硬盘、内存性能相对CPU要好很多,CPU要读/写I/O(硬盘/内存),I/O在很短的时间就可以完                                                成,而CPU还有许多运算要处理,CPU Loading很高。

             如果上面对IO和CPU密集型应用解释不好理解,简单讲如果你的应用大部分是I/O读写操作,很少复杂高频的运算,cpu        大部分是等待I/O运算,那么你的应用就是IO密集型应用;如果你的应用很少读写操作,基本都是高频且复杂的各种运算,cpu大部分时间都很忙,那么你的应用是CPU密集型应用。一般情况我们的应用都是I/O密集型应用。

       另外一种说法,是ThreadPoolExecutor线程池设置和实际并发量有关系。

      1、先简单说下线程池ThreadPoolExecutor的几个参数的意思:

      corePoolSize:核心线程数

                        核心线程会一直存活,及时没有任务需要执行

                        当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理

                        设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭

    queueCapacity:任务队列容量(阻塞队列)

                      当核心线程数达到最大时,新任务会放在队列中排队等待执行

     maxPoolSize:最大线程数

                        当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务

                        当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常

     keepAliveTime:线程空闲时间

                      当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize

                       如果allowCoreThreadTimeout=true,则会直到线程数量=0

     allowCoreThreadTimeout:为true时允许核心线程超时,即核心线程空闲时间超时也可以退出

     rejectedExecutionHandler:任务拒绝处理器

ThreadPoolExecutor执行顺序:

       当线程数小于核心线程数时,来了新任务就创建新线程,即使有空闲线程。

       当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。

       当线程数大于等于核心线程数,且任务队列已满

                   若线程数小于最大线程数,创建线程

                  若线程数等于最大线程数,抛出异常,拒绝任务

2、再说下如何设置线程池的参数,需要根据几个值来决定

        tasks :每秒的任务并发数,假设为500~1000

        taskcost:每个任务花费时间,假设为0.1s(可以依据soapUI的平均时间avg参数)

        responsetime:系统允许容忍的最大响应时间,假设为1s

     做几个计算

    corePoolSize = 每秒需要多少个线程处理? 

                 threadcount = tasks/(1/taskcost) =tasks*taskcout =  (500~1000)*0.1 = 50~100 个线程。corePoolSize设置应该大于50

                根据8020原则,如果80%的每秒任务数小于800,那么corePoolSize设置为80即可

    queueCapacity = (coreSizePool/taskcost)*responsetime

              计算可得 queueCapacity = 80/0.1*1 = 80。意思是队列里的线程可以等待1s,超过了的需要新开线程来执行

             切记不能设置为Integer.MAX_VALUE,这样队列会很大,线程数只会保持在corePoolSize大小,当任务陡增时,不能新             开线程来执行,响应时间会随之陡增。

maxPoolSize = (max(tasks)- queueCapacity)/(1/taskcost)

           计算可得 maxPoolSize = (1000-80)/10 = 92

       (最大任务数-队列容量)/每个线程每秒处理能力 = 最大线程数

rejectedExecutionHandler:根据具体情况来决定,任务不重要可丢弃,任务重要则要利用一些缓冲机制来处理

keepAliveTimeallowCoreThreadTimeout采用默认通常能满足

总结:到底线程数设置根据什么依据设置,需要各位根据自己机器情况和实际并发量,都测试下,看那个更好选择哪种设置方案。一般情况,我比较关注三个参数来判断线程池是否配置合理,并发量、avg、tps、cpu使用率。

 假如并发量在50-150之间,我的线程执行时间大概是5ms左右,tps在65-200,cpu使用率在30%-70%,这个数据就不是很理想。当然这些测试是在本地笔记本并非服务器上。

猜你喜欢

转载自blog.csdn.net/dhklsl/article/details/84344519