Executor框架知识梳理
Executor框架
一、什么是Executor框架?
为了把工作单元与执行机制分离开,Executor框架诞生了,他是一个用于统一创建与运行的接口。Executor框架实现的就是线程池的功能。
首先,我们先介绍Executor接口,如下示例:
//Executor接口
public interface Executor {
void execute(Runnable command);
}
Executor接口提供的方法将任务的提交过程与执行过程解耦开来,并用Runnable来表示任务。
Executor基于生产者-消费者模式,提交任务的操作相当于生产者(生成待完成的任务),执行任务的线程则相当于消费者(执行完这些任务)。如果要再程序中实现一个生产者-消费者的设计,那么最简单的方式通常就是使用Executor。
二、Executor框架结构详解
Executor包括3大部分:
- 任务:也就是工作单元,包括被执行任务需要实现的接口:Runnable接口或者Callable接口。
- 任务的执行策略:也就是把任务分配给多线程之后的执行机制,包括Executor接口及继承自Executor接口的ExecutorService接口。
- 异步计算任务的结果:包括Future接口及实现了Future接口的FutureTask类。
Executor框架的成员关系图:
Executor或ExecutorService对象的实例化:
Executor以及ExecutorService可以通过调用Executors(注意不是Executor)的静态工厂方法来实例化。
1.newFixedThreadPool: newFixedThreadPool将创建一个固定长度的线程池(ExecutorService对象),每当提交一个任务时就创建一个线程,直到达到线程池的最大数量,这时线程池的规模将不再变化。(如果某个线程由于发生未预期的Exception而结束,那么线程池会补充一个新的线程)
2.newCachedThreadPool: newCachedThreadPool将创建一个可缓存的线程池(ExecutorService对象),如果线程池的当前规模超过了处理需求时没那么将回收空闲的线程,而当需求增加时,则可以添加新的线程,线程池的规模不存在任何限制。
3.newSingleThreadExecutor: newSingleThreadExecutor是一个单线程的ExecutorService,它会创建单个工作者来执行任务,如果这个线程异常结束,会创建另一个线程来替代。newSingleThreadExecutor能确保依照任务在队列中的顺序来串行执行。
4.newScheduledThreadPool: newScheduledThreadPool创建了一个固定长度的线程池(ScheduledExecutorService对象),而且以延迟或者定时的方式来执行任务,类似Timer。
使用方法如下:
class Test {
private static final Executor exec = Executors.newFixedThreadPool(100);
public static void main(String []args) throws IOException {
exec.execute(new Runnable() {
@Override
public void run() {
System.out.println("GuYan");
}
});
}
}
Executor的生命周期:
为了管理执行任务的生命周期,Executor拓展了ExecutorService接口,添加了一下用于生命周期管理的方法,如下程序段:
//ExecutorService中的生命周期管理方法
public interface ExecutorService extends Executor {
void shutdown();//正常关闭ExecutorService
List<Runnable> shutdownNow();//强制关闭ExecutorService,使用时首先关闭正在执行的任务,然后返回所有尚未启动的任务清单
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
}
ExecutorService的生命周期有3种状态:运行、关闭和已终止。
ExecutorService在初始创建时处于运行状态。shutdown方法将执行平缓的关闭过程:不再接受新的任务,同时等待已经提交的任务执行完成——包括那些还未开始执行的任务。shutdownNow方法将执行粗暴的关闭过程:它将尝试取消所有运行中的任务,并且不再启动队列中尚未开始执行的任务。
在ExecutorService关闭后提交的任务将由“拒绝执行处理器”来处理,它会抛弃任务,或者使得execute方法抛出一个未检查的RejectedExecutionException。等所有任务都完成后,ExecutorService将转入终止状态。
可以调用awaitTermination来等待ExecutorService到达终止状态,或者通过调用isTerminated来轮询ExecutorService是否已经终止。
通常在调用awaitTermination之后会立即调用shutdown,从而产生同步地关闭ExecutorService的效果。
Executor框架的使用示意图:
框架使用步骤:
(1)创建Runnable并重写run()方法或者Callable对象并重写call()方法:
class callableTest implements Callable<String> {
@Override
public string call() {
try {
String name = "GuYan";
return name;
}
catch (Execption e) {
e.printStackTrace();
return "exception";
}
}
}
(2)创建Executor接口的实现类ThreadPoolExecutor类或者ScheduledThreadPoolExecutor类的对象,然后调用其execute()方法或者submit()方法把工作任务添加到线程中,如果有返回值则返回Future对象。
其中Callable对象有返回值,因此使用submit()方法;而Runnable可以使用execute()方法,此外还可以使用submit()方法,只要使用callable(Runnable task)或者callable(Runnable task, Object result)方法把Runnable对象包装起来就可以,使用callable(Runnable task)方法返回的null,使用callable(Runnable task, Object result)方法返回result。
ThreadPoolExecutor tpe = new ThreadPoolExecutor(5, 10,
100, MILLISECONDS, new ArrayBlockingQueue<Runnable>(5));
Future<String> future = tpe.submit(new callableTest());
(3)调用Future对象的get()方法后返回call方法中的结果,或者调用Future对象的cancel()方法取消当前线程的执行,最后关闭线程池。
Executor执行的任务有4个生命周期阶段:创建、提交、开始和完成。
Executor框架中,已提交但尚未开始的任务可以取消,但对于那些已经开始执行的任务,只有当它们能响应中断时,才能取消。取消一个已经完成的任务不会有任何影响。
Future中get方法的行为取决于任务的状态(尚未开始、正在运行、已完成)。如果任务已经完成,那么get会立即返回或者抛出一个Exception。如果任务没有完成,那么get将阻塞并直到任务完成。
如果任务抛出了异常,那么Future中get将异常封装为ExecutionException并被get抛出。如果任务被取消,那么get将抛出CancellationException。如果get抛出了ExecutionException,那么可以通过Future中的getCause来获得被封装的初始异常。
try {
System.out.println(future.get());
}
catch (Exception e) {
e.printStackTrace();
}
finally {
tpe.awaitTermination();
tpe.shutdown();
}
三、Executor框架成员
Executor框架成员:ThreadPoolExecutor实现类、ScheduledThreadPoolExecutor实现类、Future接口、Runnable和Callable接口、Executors工厂类