JDK1.8中的线程池
上面这段代码一直在用,面试的时候也经常被问到,却从未深究过,不知道线程池到底是怎么回事,今天看看源代码,一探其究竟
线程池主要控制的状态是ctl,它是一个原子的整数,其包含两个概念字段:
- workerCount:有效的线程数量
- runState:线程池的状态
为了在一个整型值里面包含这两个字段,我们限制workerCount最多2的29次方减1
runState的值有这样几种:
- RUNNING: 接受新的任务,并处理队列中的任务
- SHUTDOWN:不接受新的任务,继续处理队列中的任务
- STOP: 不接受新的任务,也不处理队列中的任务,并且中断正在处理的任务
- TIDYING: 所有任务都结束了,workerCount是0,通过调用terminated()方法转换状态
- TERMINATED:terminated()方法已经完成
状态之间的转换时这样的:
RUNNING -> SHUTDOWN
调用shutdown()方法,或者隐式的调用finalize()方法
(RUNNING or SHUTDOWN) -> STOP
调用shoutdownNow()方法
SHUTDOWN -> TIDYING
当队列和池都是空的时候
STOP -> TIDYING
当池是空的时候
TIDYING -> TERMINATED
当terminated()方法调用完成时
Integer.SIZE=31
Integer.SIZE - 3 = 29
所以,COUNT_BITS = 29
高3位存储runState
接下来看最复杂的那个构造方法
参数详解
- corePoolSize:保持在池中的线程数(PS:即使它们处于空闲状态)
- maximumPoolSize:池中允许的最大线程数
- keepAliveTime:当线程数超过核心线程数的时候,超出的线程的最大生存时间
- unit:keepAliveTime的单位
- workQueue:维护待处理的任务的队列
- threadFactory:创建线程的工厂
1、如果正在运行的线程数少于核心线程数,则新建一个线程去运行这个任务
2、如果工作队列没有满,则放到工作队列中
3、如果工作队列已满(PS:workQueue.offer(command)返回false),则再新建线程
4、若线程数已经达到最大线程数则reject(command)
在前面execute方法中,有3处调用了addWork()方法
第一处,如果当前有效线程数少于核心线程数,则调用之,此时线程数的边界是核心线程数
第二处,如果当前有效线程数超过核心线程数,并且将新任务放到队列中,此时有效线程数是0,则创建一个新线程
第三处,如果当前有效线程数超过核心线程数,并且队列已经放满了,并且有效线程数小于最大线程数,此时调用以创建新线程,
当前的有效线程都在works里面,而works里面放的是Worker对象,接下来看Worker
前一步中,线程的start()方法,线程运行的时候执行run()方法,而Worker继承Runnable并重新run()方法,run()方法又调用外部的runWorker()方法,所以,在线程池中新创建的线程运行时调用的是runWorker()方法
runWorker()方法循环的从队列中取出任务并执行它。也就是说,池中所有的线程都是在创建的时候如果传进来新任务,则先执行新任务,然后循环从队列中取出任务并执行
接下来,看Executors中常见的几种线程池的区别
可以看到
newSingleThreadExecutor:核心线程数和最大线程数都是1,队列是LinkedBlockingQueue
newFixedThreadPool:核心线程数和最大线程数相等,超过核心线程数的空闲线程生存时间是0,队列是LinkedBlockingQueue
newCachedThreadPool:核心线程数是0,最大线程数是Integer.MAX_VALUE,空闲线程生存时间是1min,队列是SynchronousQueue
举例说明
栗子1
假设,newFixedThreadPool的时候固定线程是2,所有,最大线程和核心线程都是2,而它的工作队列用的是LinkedBlockingQueue。
当第1个任务提交过来,新创建一个线程并执行任务,此时线程池中有1个线程;
当第2个任务提交过来,再新建一下线程并执行任务,此时线程池中有2个线程;
当第3个任务提交过来,放入工作队列中,然后由线程池中的两个线程轮询队列处理
当第n个任务提交过来,仍然放入工作队列
栗子2
假设,newCachedThreadPool,需要注意的是,它的工作队列是SynchronousQueue,这种队列的特性是一个线程向队列中放元素的时候必须同时另一个线程从队列中取出元素,否则是放不进去的
当第1个任务提交过来,线程池中的线程数为0,于是,新建一个线程,并执行任务,此时,线程池中有1个线程;
当第2个任务提交过来,由于无法放到队列中,于是再创建一个线程,此时线程池中有2个线程;
当第3个任务提交过来,若前两个线程有一个处理完了,这个时候由于是循环从队列中取,所有可以放到工作队列中