1.概览
功能简介:
- ThreadPoolExecutor就是普通的线程池。
- ScheduledThreadPoolExecutor特别适用于延迟执行任务:以固定间隔调度任务或者以固定延迟调度任务
- ForkJoinPool适用于使用分治法(Divide-and-Conquer Algorithm)能够解决的问题,特点是工作密取。
2.线程池ThreadPoolExecutor的架构
2.1 线程池四大构件
- 首先就是线程池本身:ctl状态管理、执行任务execute及submit、线程增加和缩减、以及shutdown和shutdownNow操作
- 线程本身Worker,本身实现了AQS,在执行任务加锁,屏蔽了中断
- 阻塞队列,从队列中取任务getTask
- 任务FutureTask
2.2 任务两级调度模型
2.3 Executor框架的三大部分
- 1.任务——Runnable Callable
- 2.任务的执行——ThreadPoolExecutor、ScheduledThreadPoolExecutor
-
3.异步计算的结果——Future(FutureTask)
3.ScheduledThreadPoolExecutor
3.1 Leader-Follower模式
参考并发容器BlockingQueue - DelayQueue及Leader-Follower模式
leader是等待队列头部元素的指定线程。Leader-Follower模式的这种变体用于最小化不必要的定时等待。
- 当一个线程称为leader时,其会定时等待下一个delay元素过期,但是其他线程会无限期等待。
- 当从take/poll返回之前,leader线程必须signal其他等待线程,除非在此期间有线程称为了新的leader。
- 每当队列头部元素被更早到期的元素替换时,leader被置为null,offer里面q.peek() == e时,会将leader=null,此时当然会signal,重新竞选leader。所以定时等待线程必须要处理失去leader时情况。
3.2 ScheduledThreadPoolExecutor四大构件
- 首先就是线程池本身:执行任务execute及submit被覆盖了以实现周期任务,增加了run-after-shutdown参数来处理线程池关闭后怎么处理周期任务
- 线程还是沿用Worker,本身实现了AQS,在执行任务加锁,屏蔽了中断
- 阻塞队列使用的是定制的DelayedWorkQueue,优先队列,ScheduledFutureTask会记录其在堆数组中索引,这会消除在取消时查找任务的操作,大大加快了移除操作。但是在siftUp和siftDown中会增加维护索引的额外操作。
- 任务是继承自FutureTask的ScheduledFutureTask,实现了compareTo(基于time和序列号),方便放入DelayedWorkQueue。通过period区分是一次性任务还是周期性任务。通过setNextRuntime区分是scheduleAtFixedRate还是scheduleWithFixedDelay。
4.ForkJoinPool
4.1 四大构件
-
线程池ForkJoinPool,核心的ctl打包了AC、TC、SS和ID,runState用于操作workQueues数组。
-
工作线程ForkJoinWorkerThread,每个工作线程都会注册一个自己的WorkQueue,该WorkQueue放在workQueues数组奇数索引处。线程执行时,不一定会执行当前 WorkQueue 中的任务,而是可以偷取别的Worker的任务来执行。
-
队列WorkQueue,支持工作窃取和外部任务提交的双端队列,只支持四种可能的端操作中的三种 - push,pop和poll(也称为steal),进一步的限制是push和pop仅从拥有的线程调用,而poll可以从其他线程调用。
-
任务ForkJoinTask。
1)任务类型
RecursiveAction——不返回结果的计算
RecursiveTask——返回结果
CountedCompleter——已完成的操作触发其他操作
2)内部提交
fork——直接加入到当前线程的workQueue中
invoke——提交任务等待任务完成并获取结果(当前线程执行)
join——等待任务完成并获取结果,尝试在当前线程中开始执行。
3)任务状态
当状态完成时,为负数,表示正常完成、取消或者异常;
阻塞等待的任务设置了SIGNAL
4.2 整体的运行机理
-
ForkJoinPool适合分治算法
-
任务提交分为外部和内部提交
-
整体运行图
网友评论