Java基础系列--Executor框架(一)
原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/8393618.html
一、Executor框架介绍
Executor框架是JDK1.5之后出现的,位于juc包中,是并发程序设计的工具之一。各个版本以来一直在进行修正。
Executor是执行者之意,表示任务的执行者,这里的任务指的是新的线程任务(实现Runnable接口的执行任务)。
整个Executor执行者框架包括多个接口和类,甚至还涉及到阻塞队列的使用,协同实现任务的执行。
下面是简单的Executor框架的类结构:
从上面的类结构中我们可以看到Executor接口是整个框架的祖接口,它大致规划了框架的结构,并定义了执行方法execute(),这个方法需要一个Runnable作为入参,表示执行一个线程任务。从这里也可以看出来这个框架的主要思想:将要执行的任务和具体的执行进行解耦,任务的内容单独定义为一个线程,任务的执行交给该框架进行,只需要将任务提交给框架即可(这个后面会提到)。Runnable入参就表示定义为单独线程的任务内容,execute方法则是执行任务,整个框架定义的就是这样一个任务执行器,Executor框架总的来说就是一个多线程任务执行框架。
二、Executor接口
Executor接口是整个框架的总接口,正如上面所述,它描述了框架的主要实现思想:任务内容与执行的解耦。其源码很短,我们可以看看:
1 package java.util.concurrent; 2 public interface Executor { 3 4 /** 5 * Executes the given command at some time in the future. The command 6 * may execute in a new thread, in a pooled thread, or in the calling 7 * thread, at the discretion of the {@code Executor} implementation. 8 * 9 * @param command the runnable task 10 * @throws RejectedExecutionException if this task cannot be 11 * accepted for execution 12 * @throws NullPointerException if command is null 13 */ 14 void execute(Runnable command); 15 }
这是一个单独的接口,其内部只有一个execute()方法。我们看看其注释:在将来某一时刻执行给定的指令,可能会在一个新的线程、或一个线程池中的线程、或在正调用的线程中执行,这取决于Executor接口的具体实现。
注意:这个方法中的入参任务指令是必不可少的,不可传null,否则会报NullPointerException(空指针异常)。
三、ExecutorService接口
ExecutorService接口继承了Executor接口,Executor接口仅仅描述了思想,定义了一个执行器,ExecutorService接口在其基础上进一步丰富了框架的接口,为框架定义了更多内容,包括:任务的提交,执行器的终止关闭等。
3.1 终止方法
1 void shutdown(); 2 List<Runnable> shutdownNow();
如上源码,ExecutorService中定义了两个终止方法,这两个方法并不完全相同,第一个方法shutDown()的作用是终止新任务的接收,已接收的任务却需要继续执行。这是保证已提交任务全部执行的终止方法。第二个shutDownNow()方法属于强效终止方法,它会试图停止正在执行的线程任务,并且不再执行处于等待状态的其他任务,并且会将这些任务以列表的方式返回。
注意:第二个方法的试图停止,并不一定会停止,因为其实现会使用Thread.interrupt()方法来进行线程任务中断执行,但是如果任务线程不会响应该中断,则不会被终止。
3.2 任务提交方法
1 <T> Future<T> submit(Callable<T> task); 2 <T> Future<T> submit(Runnable task, T result); 3 Future<?> submit(Runnable task);
这三个任务提交方法采用方法重载的方式定义,其实均是对execute方法的再封装,对其进行扩展而来。因为execute方法只能接受Runnable入参,切无返回值。submit提交任务却拥有返回值,而且可以接收两种格式的任务,Callable和Runnable两种。不同的方法参数和返回值也略有不同。
第一种方法接收一个Callable入参,任务执行成功会返回一个表示该任务的Future,通过其get方法可获取到在Callable任务中指定的返回内容。
第二种方法接收一个Runnable入参和一个指定的返回值,任务执行成功会返回一个表示该任务的Future,通过其get方法可以获取到之前的入参result的值,即入参result即为预设的返回值。
第三种方法接收一个Runnable入参,任务执行成功会返回一个表示该任务的Future,通过get方法可得到null。
我们通过下面的实例来进行验证:
1 public static void main(String[] args) throws Exception { 2 ExecutorService executor = Executors.newCachedThreadPool(); 3 //第一种方法:入参为Callable 4 Future<String> result1 = executor.submit(new Callable<String>() { 5 @Override 6 public String call() throws Exception { 7 return "task2";//此处的task2即为返回的内容,即future.get()的值 8 } 9 }); 10 //第二种方法:入参为Runnable和T 11 Future<String> result2 = executor.submit(new Runnable() { 12 @Override 13 public void run() { 14 System.out.println("mmp"); 15 } 16 },"task1");//此处的task1即为返回的内容,即future.get()的值 17 //第三种方法:入参为Runnable 18 Future<?> result3 = executor.submit(new Runnable() { 19 @Override 20 public void run() { 21 System.out.println("nnd"); 22 } 23 }); 24 System.out.println(result1.get()); 25 System.out.println(result2.get()); 26 System.out.println(result3.get()); 27 }
执行结果为:
task2 mmp task1 nnd null
从上面的结果中也可以看出三个方法的不同之处。
3.3 invokeAny方法
1 <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; 2 <T> T invokeAny(Collection<? extends Callable<T>> tasks,long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
第一种方法表示执行给定的任务列表中的任务,如果某个任务成功完成,没有任何异常,则将该任务的结果返回,一旦成功或者异常被返回之后,任务列表中其他任务则取消执行,那么可以看出返回的必定是第一个执行成功的任务的结果或者最后一个任务的执行异常。
第二个方法是在第一个方法的基础上加上一个超时限制,如果在超时期满之前完成了某个任务则返回该任务的结果,其余同上。
注意:这里写到一旦成功或者异常被返回,其实这里如果第一个任务执行的时候出现了异常,则同样会被返回,同样其他任务取消执行。
例子:
1 public static void main(String[] args) throws Exception { 2 ExecutorService executor = Executors.newCachedThreadPool(); 3 List<Callable<String>> callables = new ArrayList<>(); 4 callables.add(new Callable<String>() { 5 @Override 6 public String call() throws Exception { 7 return "task1"; 8 } 9 }); 10 callables.add(new Callable<String>() { 11 @Override 12 public String call() throws Exception { 13 return "task2"; 14 } 15 }); 16 callables.add(new Callable<String>() { 17 @Override 18 public String call() throws Exception { 19 return "task3"; 20 } 21 }); 22 callables.add(new Callable<String>() { 23 @Override 24 public String call() throws Exception { 25 return "task4"; 26 } 27 }); 28 String s = executor.invokeAny(callables); 29 System.out.println(s); 30 }
执行结果:
task1