在Executor框架中,工作单元包括Runnable和Callable,执行机制由Executor框架提供。
两层调度模型
多线程程序将任务分解为多个任务,然后由用户级调度器Executor将这些任务交由Java线程执行。Java线程并不是直接被CPU调度执行,还会映射为操作系统内核线程,由内核调度器将内核线程调度到CPU执行。祥见Java线程和os线程
Exectuor框架的结构
Executor框架由三大部分组成:
- 任务,包括被执行任务需要实现的接口,Runnable接口和Callable接口;
- 任务的执行,任务机制的和讯即接口及其实现类,ThreadPoolExecutor和ScheduledThreadPool;
- 异步计算的结果,Future及其实现类FutureTask;
Executor框架的使用
主线程创建一个Runnable或者Callable任务(Executor可以将Runnable类型转换为Callable类型),然后交给Executor执行,主线程通过返回的Future接口,阻塞等待任务执行以后返回结果,也可以在等待过程中取消任务执行。
Executor框架核心类
- ThreadPoolExecutor,线程池的核心实现类,用来执行被提交的任务。通过工厂类Executors实现三种类型线程池:
1 | //核心线程数和最大工作线程数量相等,避免创建大量线程,适用于服务器负载较重的情况。 |
ScheduledThreadPoolExecutor用于在固定延迟后执行任务,通过Executors创建,包括两种类型:
1 | //只会创建单个核心线程,工作线程为Integer.MAX_VALUE |
Runnable接口和Callable接口的实现类,都可以交个线程池执行,不同的是Callable接口可以返回结果.也可以将一个Runnable对象封装为Callable对象:
1 | public static <T> Callable<T> callable(Runnable task, T result) { |
ScheduledThreadPoolExecutor
1 | //使用DelayWorkerQueue作为工作队列,这是一个无界阻塞队列,使用PriorityQueue实现。 |
主线程向DelayWorkerQueue中添加任务时,任务会被包装为ScheduledFutureTask,线程池中的线程会从队列中取出任务执行。
ScheduledThreadPoolExecutor的实现
1 | ScheduledFutureTask(Callable<V> callable, long ns) { |
DelayQueue中的任务是ScheduledFutureTask类型,包括三个成员变量。DelayQueue封装了一个PriorityQueue,会根据ScheduledFutureTask的time和sqquenceNumber进行排序。线程池中的线程会从任务队列中取出time大于当前时间的任务进行执行。在执行结束以后,会更新time,重新将任务放回队列之中。
1 | private void setNextRunTime() { |
FutureTask
由于FutureTask继承了Future接口和Runnable接口,所以可以把一个FutureTask接口交由实现了Executor的线程池执行,也可以作为计算结果返回,然后执行FutureTask.get()阻塞当前线程等待返回计算结果。
1 | <T> Future<T> submit(Runnable task, T result); |
FutureTask是基于AbstractQueuedSynchronizer(AQS)实现的,很多可阻塞类都是基于AQS实现的,AQS是一个原子框架,提供了通用机制来原子性的管理状态,阻塞和唤醒线程,以及维护被阻塞线程的队列。对于很多阻塞类,其具体操作都会委托给实现了AQS的内部类Sync,由Sync进行具体的操作。