线程池(ThreadPoolExecutor)的继承实现结构
线程池的状态
ThreadPoolExecutor使用int的高3位来表示线程的状态,低29位来表示线程的数量。
这些信心存储在一个ctl原子变量中,目的是将线程状态和线程数合在一起,执行一次cas操作就可完成两个的赋值。
线程池的构造方法和参数
- coorPoolSize:核心线程数
- maxinumPoolSize:最大线程数
- keepAliveTime:救急线程的生存时间
- unit:时间单位,针对上一个参数
- workQueue:阻塞队列
- threadFactory:线程工厂
- handler:拒绝策略
线程池的工作流程:
-
线程池一开始并没有线程,当一个任务被提交后,线程池会创建一个线程去执行任务
-
当多个任务被提交时,如果当前线程处于工作状态,无法及时处理任务,线程池会创建其他的线程去执行任务
-
当线程池的线程数达到coorPoolSize时,当有新的任务被提交时,如果当前所有的线程都处于工作状态,那么线程池就会将新的任务加入到workQueue阻塞队列中
-
如果后续不断地有任务被提交,加入到阻塞队列中,如果阻塞队列是有界的,当阻塞队列已满时,如果有新的任务有被提交进来,则这个时候会创建救急线程来执行任务,救急线程数最多为最大线程数maxinumPoolSize减去核心线程数coorPoolSize。
-
当所有的救急线程都被创建,任务队列已满,这时仍有任务被提交,则会执行拒绝策略,JDK提供四四种拒绝策略的实现,不同的拒绝策略有不同的效果:
- AbortPolicy:让调用者抛出RejectedExecutorException异常
- CallerRunsPolicy:让调用者运行任务
- DiscardPolicy:放弃本次任务
- DisscardOldestPolicy:放弃队列中最早加入的任务,用本任务替换
-
当任务高峰过去后,如果救急线程一段时间没有任务,就会被结束掉,这个时间由keepAliveTime和unit控制
线程池工工厂
JDK的Executors类中提供了多种线程池的工厂方法,不同的方法通过调用线程池构造方法时传的不同参数值,来返回具有不同特性的线程池。
1. newFixedThreadPool
固定线程数量的线程池,我们可以看到这里的构造方法传参数时,最大线程数等于核心线程数,意味着不会创建救急线程,所以这里救急线程的存活时间也是0,同时阻塞队列也是无界队列。
特点
- 没有救急线程
- 阻塞队列是无界队列,可以一直往里面加入任务 适用于任务量已知,执行时间较长的任务
2.newCachedThreadPool
带有缓冲的线程池,我们可以看到线程池的和线程数为0,而救急线程数则是Integer的最大值,救急线程空闲时存活的时间为60s,阻塞队列采用的是没有容量的阻塞队列。
特点:
- 没有核心线程数,所有的线程都是救急线程,救急线程的空闲生存时间为60s
- 使用了SynchronousQueue作为阻塞队列,没有容量,只有当线程来取任务的时候,任务才能放进去
适用于任务比较密集,但是单个任务执行时间较短的情况。
3.newSingleThreadPool
单线程的线程池,没有救急线程,阻塞队列是无界队列
4.newScheduledThreadPool
任务调度线程池,可以定时执行任务,也可以周期执行任务
定时执行任务:
周期执行任务:
注意:如果在执行上次任务时所花的时间大于间隔的周期,则在上个任务执行完毕后立即执行下次任务
特点
- 使用了装饰器模式,之对外暴露特定的方法,可以避免某些核心方法被暴露,从而破坏单线程。
单线程线程池和自己创建线程的区别
自己创建的线程在出现异常时会终止执行,不会有补救措施,而单线程线程池在执行期间出现异常则会创建一个新的线程,保证后续任务的执行。
提交任务
线程池中定义了多种提交任务的方法:
关闭线程池
shutdown()
执行此方法发线程池的状态会变为SHUTDOWN,不会接受新的任务,但是已提交的任务会执行完,调用此方法不会阻塞线程的执行。
shutdownNow()
线程池状态变为STOP,会用interrupt中断正在执行的任务,不再接受新的任务,会返回阻塞队列中的任务。