public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
......
}
有两个重要的参数设置:核心线程数corePoolSize,最大线程数maximumPoolSize。
线程和哪些因素有关?
CPU
在最开始介绍多线程《多线程的由浅及深》时,介绍到线程共享进程的上下文环境,为更细粒度的CPU时间段。所以线程数的确定和CPU有关。至于CPU的核数和线程数的关系,可以查看这篇文章:CPU的核心数、线程数的关系和区别。(多线程实际上是计算机多种资源的并行运用,跟CPU有几个核心是没什么关系的)IO
IO分为磁盘IO和网络IO。影响磁盘的关键因数是磁盘服务时间,即磁盘完成一个I/O请求所花费的时间,它由寻道时间、旋转延迟和数据传输时间三部分构成。衡量其关键指标,大致是IOPS、吞吐量等。影响网络IO的关键因素是服务器响应延时 + 带宽限制 + 网络延时 + 跳转路由延时 + 本地接收延时。并行
多个cpu实例或者多台机器同时执行一段处理逻辑并发
CPU不断切换线程来实现多路复用,以提升效率。通过cpu调度算法,看上去同时执行,实际上从cpu操作层面不是真正的同时。通常会用TPS或者QPS。
任务的性质
-
CPU密集型任务
要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。一般配置线程数=CPU总核心数+1 (+1是为了利用等待空闲)
-
IO密集型任务
这类任务的CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。常见的大部分任务都是IO密集型任务,比如Web应用。对于IO密集型任务,任务越多,CPU效率越高(但也有限度)。一般配置线程数=CPU总核心数 * 2 +1
总结
根据并发编程网的《如何合理地估算线程池大小》一文中的提示,
最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目
所以线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程
问题
-
是否使用线程池就一定比单线程效率高呢?
否。比如Redis(点击查看)。
-
并发编程网的一个问题:
2.1 高并发、任务执行时间短的业务怎样使用线程池?
线程池线程数可以设置为CPU核数+1,减少线程上下文的切换
2.2 并发不高、任务执行时间长的业务怎样使用线程池?
a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以适当加大线程池中的线程数目,让CPU处理更多的业务 b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,线程池中的线程数设置得少一些,减少线程上下文的切换
2.3 并发高、业务执行时间长的业务怎样使用线程池?
并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,以及线程池的设置。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。