线程池的实现原理
调用方不断地向线程池中提交任务;线程池中有一组线程,不断地从队列中取任务,这是一个典型的生产者-消费者模型。
ThreadPoolExecutor/ScheduledThreadPoolExecutor都是基于阻塞队列来实现的,而不是一般的队列,至此,各式各样的阻塞队列就要派上用场了。
在这里有两个核心的类,ThreadPoolExecutor和ScheduledThreadExecutor,后者不仅可以执行某个任务,还可以周期性地执行任务。
向线程池中提交每个任务,都必须实现Runnable接口,通过上面的Executor接口中的execute(Runnable command)向线程池提交任务。
然后再ExecutorService中,定义了线程池的关闭接口shutdown(),还定义了可以有返回值的任务,也就是Callable。
核心配置参数解释
- corePoolSize:在线程池中始终维护的线程个数。
- maxPoolSize:在corePoolSize已满,队列也满的情况下,扩充线程至此值。
- keepAliveTime/TimeUnit:maxPoolSize中的空闲线程,销毁所需要的时间,总线程数收缩回corePoolSize。
- blockingQueue:线程池所用的队列类型。
- threadFactory:线程创建工厂,可以自定义,也有一个默认的。
- RejectedExecutionHandler:corePoolSize已满,队列已满,maxPoolSize已满,最后的拒绝策略。
处理流程如下:
- 判断当前的线程数是否大于或等于corePoolSize。如果小于,则新建线程执行;如果大于,则执行2。
- 判断队列是否已满,如果未满,则放入;如已满,则进入3。
- 判断当前线程数是否大于或等于maxPoolSize,如果小于,则新建线程执行,如果大于,则进入4
- 根据拒绝策略,拒绝任务。
线程池的4种拒绝策略
在execute的最后,调用了reject执行拒绝策略。
RejectedExecutionHandler是一个接口,定义了四种实现,分别对应四种不同的拒绝策略,默认是AbortPolicy。四种策略的如下:
- 让调用者直接在自己的线程里面执行,线程池不做处理
- 线程池直接抛出异常
- 线程池直接把任务丢掉,当作什么都没发生
- 把队列里面最老的任务删除掉,把该任务放入队列中
Callable与Futrue
execute接口是无返回值的,与之对应的是一个有返回值的接口Future submit。
ScheduledThreadPoolExecutor
ScheduledThreadPoolExecutor实现了按时间调度来执行任务,具体而言有两个方面:
(1)、延迟执行任务
(2)、周期执行任务
这两类型的区别如下:
AtFixedRate:按固定频率执行,与任务本身执行时间无关。但有个前提条件,任务执行时间必须小于间隔时间。
WithFixedDelay:按固定间隔执行,与任务本身执行时间有关。例如,任务本身执行时间是10s,间隔2s,则下一次开始执行的时间就是12s.
延时执行和周期执行的原理
延时执行依靠的是DelayQueue。而周期性执行任务是执行完一个任务之后,再把任务仍回到任务队列中,如此就可以对一个任务反复执行。