JDK5之前,工作单元和执行机制是混合的,JDK以后,将两者分开:
- 工作单元:Runable、Callable定义
- 执行机制:Executor框架负责
Executor框架的两极调度模型
用户程序通过Executor框架将任务映射为固定线程
操作系统将用户线程映射为内核线程,CPU处理
Excutor框架的结构概览
三大组成部分
- 任务:Runable、Callable
- 任务调度执行:实现了ExecutorService的两个执行器,ThreadPoolExecutor和ScheduledThreadPoolExecutor
任务执行结果:Future接口,主要是实现Future接口的FutureTask
整体类图
使用流程图
任务执行器
ThreadPoolExecutor
- FixedThreadPool
- SingleThreadExecutor
- SingleThreadExecutor
ScheduledThreadPoolExecutor
Callable
- Runable
任务结果
Future接口
FixedThreadPool详解
- 提交一个任务,通过offer到SyncQueue中
- 若有空闲的线程poll到了,就直接分配给这个线程
- 若没有线程poll,则立即新建线程,进行poll操作
ScheduledThreadPoolExecutor详解
- 调用scheduleAtFixedRate()方法或者scheduleWith-FixedDelay()方法,会先延迟执行器中的DelayQueue中添加一个实现了RunnableScheduledFutur接口的ScheduledFutureTask。
- 执行图
3.执行周期的实现
后面再说
ScheduledThreadPoolExecutor的实现
- 队列中元素:ScheduledFutureTask
ScheduledFutureTask
- long time:任务执行的具体时间
- long sequenceNumber:被加入队列的序列号
- long period:任务执行间隔周期
- DelayQueue.take()获取一个到期任务
- 线程执行任务,并重设time=time+周期时间
-
DelayQueue中take的任务过程
获取Lock锁
- 获取周期任务
- 若PriorityQueue空时,Condition等待
- 若有元素,且队头元素时间大于当前时间,await到那个time
- 可以获取到元素后,若队列不为空,唤醒所有线程
- 释放锁
DelayQueue队列的add流程
- 获取Lock
- 添加任务
- 向PriorityQueue添加任务
- 若是头元素,唤醒所有等待的线程
- 释放Lock
FutureTask详解
FutureTask除了实现Future接口,还实现了Runnable接口。因此FutureTask交给Executor后,调用线程直接执行 FutureTask.run()
FutureTask的3种状态
- 未启动:FutureTask.run()没有被执行之前
- 已启动:调用了run但是没有运行完毕
已完成
get方法
- 未启动或已启动:会阻塞线程
- 已完成:抛出异常或返回结果
- cancel方法
- cancel(true)
- 未启动:任务将永远不被执行
- 已启动:已中断方式试图停止
- cancel(false)
- 未启动:同上
- 已启动:正常运行完毕
- 已完成的任务:cancel方法统一返回false
- cancel(true)
FutureTask的使用
- 产生:ExecutorService.submit()
- 可执行的操作
- get
- cancel
FutureTask的实现
get的执行流程
- 调用AQS.acquireSharedInterruptibly(int arg)
- 成功,直接放回结果
- 失败,进入等待队列,进入第2步
- 其他线程release后,唤醒线程,当前线程获取后,离开等待队列,并唤醒后继线程
- 最后返回结果或抛异常
所有调用get方法的线程会进入等待队列。 任务完成后,会先唤醒队头队列,之后依次传播唤醒
run方法流程
- 执行Callable.call
- 原子更新状态,成功,则设置call()放回的结果,再AQS.release
- 调用done()方法