Thread提供了线程的实现,Executor(点击查看源码) 提供了任务被执行的策略接口,那么更深一步的任务执行的整个过程和生命周期,又是如何来体现的呢? 接下来,我们看看ExecutorService
上源代码:
public interface ExecutorService extends Executor { void shutdown(); List<Runnable> shutdownNow(); boolean isShutdown(); boolean isTerminated(); boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException; <T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task); <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException; <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException; <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException; <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
void shutdown();
void shutdown();
解读:将执行平缓的关闭过程:
不再接受新的任务
同时等待那些已经提交的任务完成
包括等待那些尚未开始执行的任务到全部完成
注意:如果在执行的某些线程无法结束,例如一直处于循环或者阻塞或者死锁等异常,那么该方法将会一直等待线程执行完成。这是不是很糟糕呢?
List<Runnable> shutdownNow();
List<Runnable> shutdownNow();
返回: 那些还没有来得及被执行的任务
解读: 将执行粗暴的关闭过程:
不再接受新的任务
尝试取消所有运行中的任务,就是发送interrupt信号
不再启动队列中尚未开始的任务
注意:比较上一个方法shutdown,这个会更霸气一点,并且会直接尝试取消运行中的任务,注意尝试指的是Interrupt。所以如果某个线程,无法响应Interrupt,那么这个方法仍然无法达到预期的目标。所以切记再写runnable的时候要记得响应InterrptException,否则会导致线程池无法正常回收。
是不是很想再加一个shutdownRightNow()的方法?
boolean isShutdown();
boolean isShutdown();
返回:是否正在关闭,只有当执行过shutdown或者shutdownNow时才会返回true.
boolean isTerminated();
boolean isTerminated();
返回: 所有任务是否已经全部结束了
注意: 可能会永远返回false哦,想想为什么
shutdown或者shutdownNow从为被执行过时,将永远返回false
shutdown或者shutdownNow被执行过,但又线程永远未能正常结束,将永远返回false
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
参数: 超时时间和时间单位
返回: 所有任务是否已经全部结束了
异常: InterruptedExcetpion,当阻塞等待中,线程被Interrupt
解读: 阻塞的方式等待所有线程被执行完成,除非
所有任务都完成了
Timeout,超过了最大等待时间
本线程被Interrupt
这三种情况下,会返回或者抛异常。
面试点:请说明线程池中 shutdown, shutdownNow, isTerminated和 awaitTermination 的各自特点和使用时的注意点。
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Callable<T> task);
参数: Callable<T> task(点击查看源码)
返回: 执行的结果Future (点击查看源码)
<T> Future<T> submit(Runnable task, T result);
<T> Future<T> submit(Runnable task, T result);
参数: Runnable task待执行的任务, T result,具体的执行结果范型
返回: 执行的结果Future
Future<?> submit(Runnable task);
Future<?> submit(Runnable task);
参数: Runnable task 被执行的任务
返回: 执行的结果Future
以上三个方法是,比较类似的三个执行当个任务的方法,再加上父类中execute方法,是不是齐了呢?带结果返回的和不带结果返回的,带指定返回参数的和不带返回参数的。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;
参数:Callable的集合 tasks.
返回:执行结果的集合Future
异常:InterruptedException, 如果该线程被中断了。
注意:所有任务很又可能会乱续执行
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;
参数:Callable的集合 tasks. 等待时间
返回:执行结果的集合Future
异常:InterruptedException, 如果该线程被中断了。
注意:所有任务很又可能会乱续执行,当
所有任务都被提交了
提交超时
将返回结果
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;
参数:Callable的集合 tasks.
返回:返回第一个执行完成的任务,其余的将被取消
异常:InterruptedException, 如果该线程被中断了。ExecutionException执行过程中的异常
注意:所有任务很又可能会乱续执行,任何一个都又可以最先执行完成。
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
参数:Callable的集合 tasks. 等待时间
返回:返回第一个执行完成的任务,其余的将被取消
异常:
InterruptedException, 如果该线程被中断了;
ExecutionException 执行过程中的异常;
TimeoutException 超时;
注意:所有任务很又可能会乱续执行,任何一个都又可以最先执行完成
几乎扩展了父接口所有执行场景提交单个还是提交多个,是否超时,是且的执行关系还是或的执行关系。
回答上期的问题:
Executor, 为什么这个接口只有一个方法。为什么ExecutorService,会去继承这个接口,而不是直接把这个方法纳入到自己的接口中?
关注点不同,Executor更关注执行的策略,而ExecutorService更关注执行当中的生命周期。
场景不同,Executor使用的场景可能会更简单,而ExecutorSerivce使用的场景会更复杂
扩展性较好,如果有其他的场景,ExecutorService并不太适合时,那么使用者可以通过自己扩展Executor来实现
通常一个好的接口设计会直接影响到你的程序设计,而如何抽象接口更是考验一个设计者功底。