ThreadPoolExecutor
线程池有多种实现类,常用的例如 固定大小的FixedThreadPool, 可以复用线程的CachedThreadPool,可以定时的ScheduleThreadPool等等。
线程池结合Executor模式,最终提供了一套简单可用的线程池。
使用线程池一定要注意内存使用,内存的使用和构造ThreadPoolExecutor时候的参数使用有很大关系
查看ThreadPoolExecutor的构造函数可以看到几个核心参数
corePoolSize 核心线程数
maximumPoolSize 最大线程数
keepAliveTime 当一个线程任务完成后的存活时间(超过核心线程的部分)
unit keepAliveTime的单位
BlockingQueue 当核心线程使用完毕后,之后的任务将会放入到此队列
ThreadFactory 线程工厂类
RejectedExecutionHandler 当线程数量达到maximumPoolSize之后的拒绝策略
参数详解
其中核心线程数和最大线程数之间的关系也比较重要,核心线程是当任务提交之后一次性创建的,最大线程是队列满了之后继续创建线程的最大值。
流程如下:
一开始,池中将会创建corePoolSize个线程
之后陆陆续续提交任务,池中的corePoolSize个线程都开始了工作。但是任务还在陆陆续续进来,这时候,任务将会被存在BlockingQueue中
之后BlockingQueue也满了,这时候将会继续创建线程,来处理任务。任务还在继续,创建的线程的数量到达了maximumPoolSize的大小,这时,后续的任务将会被reject
所以在设置Queue以及maximumPoolSize参数就需要小心,防止不断存入队列或者最大线程池大小过大
举例
以二元组为一个状态,来看看这个过程中各个参数的变化
举例来说,corePoolSize=5, maximumPoolSize=10, Queue大小为3,任务数为30,前提为任务时间比较长
{corepoolsize, queue_size}
{0, 0} -> {5, 3} -> {6, 3} -> {7, 3} -> {8, 3} -> {9, 3} -> {10, 3}
13号之后的任务被拒绝
再假如 任务数为 12
这个数量变化为:
{corepoolsize, queue_size}
{0, 0} -> {5, 3} -> {6, 3} -> {7, 3} -> {8, 3} -> {9, 3} -> {9, 3}
可以看到是最终是队列满的,线程数量停留在9