在线程池中,我们常常看到 Executor,ExecutorService,AbstractExecutorService 的出现,但是可能对于他们之间的关系,他们的作用,以及他们的注意点不太清楚
在本文中,我将简单的介绍他们的主要功能,以及简单分析他们的源码
如果有疏漏之处,请不吝赐教
1. Executor 接口
Executor 接口是线程池中最高级的接口,即所有关于线程池的类或接口都继承与该接口
该接口中只有一个方法
void execute(Runnable command);
该方法是往线程池中提交任务的方法,command 就是我们提交的任务
并且从它的返回值是 void 类型可以看出,execute() 不能得到提交任务的返回值
execute() 只接收 Runnable 对象,但是我们可以使用 RunnableFuture
来实现Callable 转换为 Runnable
2. ExecutorService 接口
Executor 接口提供了一个基础的方法:execute() ,但是这个方法实际上不能满足我们的一些需求,所以我们开发了 ExecuteService 接口来满足我们的一些其他的需求
2.1 支持 Callable 类型任务,并可以得到其返回值
ExecutorService提供了两种提交任务的方式:单一提交和批量提交
单一提交
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
ExecuteService 接口提供了三个单一提交的方法: submit()
从上面的源码中可以看出,submit() 既可以支持 Runnable ,也支持 Callable ,同时,也可以获取 Runnable 类型任务的返回值
submit() 的返回值都被封装为 Future 类型,我们可以通过 Future 的相关方法来获取返回值
并且在 ExecuteService 接口源码中没有覆写 execute() ,这意味着:execute() 只支持提交 Runnable 类型的任务
当然,我们也可以通过 Future 接口及子类来实现 execute() 支持 Callable 类型的任务
new MyExecutorService().execute(new FutureTask<Integer>(new MyCallable()));
当然,一般来说,我们都使用 RunnableFuture
接口来实现 Callable 转换为 Runnable
因为 RunnableFuture 继承于 Runnable 和 Future ,既可以把 Callable 转换为 Runnable,也可以得到返回值
如在该类中的 submit() 就使用了RunnableFuture
批量提交
有时候我们可能要提交多个任务到线程池,而 submit() 只支持提交一个任务,所以 ExecuteService 提供了批量提交的方法:invokeAll() 和 invokeAny()
<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;
这两个方法都接受Collection
接口的对象,而 List 和 Set 继承于 Collection,所以invokeAll() 和 invokeAny()可以接收 List 和 Set 接口及其子类的对象
并且在使用这两个方法的过程中,有以下的注意点
-
这两个方法只支持 Callable ,因为源码中 Collection 中只能存放 Callable 及其子类的对象
-
InvokeAll() 返回一个List类型对象,即返回所有任务的结果,并把结果放在一个 List 对象中
invokeAny() 返回 Callable 返回值类型的对象,
只返回一个任务的结果,这个结果是随机
,可能是第一个任务结果,可能是中间的一个任务结果,也可能是最后一个任务的结果Java中对这两个方法的实现代码在
AbstractExecutorService
类中,比如 ThreadPoolExecutor 中就直接用了 AbstractExecutorService 类的 invokeAll() 和 invokeAny() 的代码,所以对这两个源码的分析我放在了 AbstractExecutorService 中 -
这两个都使用了 throws ,所以使用时必须在 try catch 中
2.2 关闭线程池
-
shutdown()
关闭线程池,新进来的线程被拒绝,执行拒绝策略
不会关闭正在运行的线程
-
shutdownNow()
关闭线程池,新进来的线程被拒绝,执行拒绝策略
并且会尝试关闭正在运行的线程,在 ThreadPoolExecutor 中,是用中断来关闭的,即
interrupt()
但是这样有一个问题,
如果任务无法对 interrupt() 进行响应,那么不能关闭正在运行的线程
List<Runnable> shutdownNow();
shutdownNow() 返回一个 List 类型对象,这个 list 里面存放了未完成,但是被终止的任务,方便我们以后继续完成这些任务
这两个方法是在 ThreadPoolExecutor 类中进行了实现,所以更具体的分析会在 ThreadPoolExecutor 的博客中
2.3 查看线程池状态
-
isShutdown() 线程池是否关闭
-
isTerminated() 所有任务是否全部完成
-
awaitTermination(long timeout, TimeUnit unit)
等到所有任务全部完成
可以设置超时时间
3. AbstractExecutorService 类
因为 Executor 和 ExecutorService 都是接口,所以具体的实现实际上是在其他类中的
AbstractExecutorService 具体实现了 ExecutorService 中关于提交任务的方法
从下图中,我们可以看到,AbstractExecutorService 实际上就是实现了submit() invokeAll() invokeAny()
这三个方法
3.1 newTaskFor()
这个方法的作用实际上就是将 Callable 和 Runnable 对象转换为 RunnableFuture 对象
RunnableFuture 是 Runnable 和 Future 的子接口
3.2 submit()
submit()的实现很简单,比如下面是其中一个 submit() 的源码
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
我们可以看到,实际上就是将 Runnable 和 Callable 对象转换为 RunnableFuture 对象,然后调用 execute() 来执行,如果返回 RunnableFuture 对象
因为 RunnableFuture 继承了 Runnable 和 Future ,所以可以调用 execute(),也可以得到返回值
3.3 invokeAny()
invokeAny()实际上都是通过doInvokeAny()
来进行实现
doInvokeAny()的主体代码是在一个 try catch finally
代码块中
try 代码块中
在 try 的代码块中,有一个死循环,如下面的代码
for (;;){
Future<T> f = ecs.poll();
if (f == null) {
//...
}
if (f != null) {
//...
}
}
-
Future f = ecs.poll();
ecs 是
ExecutorCompletionService
的一个对象ExecutorCompletionService 是Java中批量执行异步任务的一个类,他内部维护一个阻塞队列,为 LinkedBlockingQueue 类型 poll() 是 ecs 的一个非阻塞的删除方法,他删除阻塞队列的第一个对象,并返回该对象
-
if (f == null)
如果返回的是null,说明任务没有执行完成,不能得到返回值,那么就会进行多层 if else 的判断
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next()));
++active;
}
ntasks 的初始值为 tasks.size(),即输入参数中集合的大小,也就是任务的个数,美次提交一个任务,ntasks 就会减一,ntasks > 0 就表示任务没有提交完 如果任务没有提交完,那么就不会进行后面的if else 判断
只有任务提交完了以后,才会进行后面的 if else 判断
提交任务后,进行下一层循环,因为前面调用了 poll(),删除了 ecs 阻塞队列中的第一个对象,所以下一层循环的 f 就是现在提交的这个
else if (active == 0)
break;
else if (timed) {
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null)
throw new TimeoutException();
nanos = deadline - System.nanoTime();
}
上面的判断是如果提交的任务全部执行完或者超时
,就跳出循环,进入 finally 代码块
else
f = ecs.take();
到达这里,说明是最后一个任务
了,并且不满足前面的条件,调用 ecs 的阻塞
方法来获取 Future 对象
- if( f != null)
if (f != null) {
--active;
try {
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
f != null ,说明有返回值了,调用 f.get() 来获取返回值 注意;
- 可能前面的 f 为 null ,到这里不为 null 了
如果任务未提交完成,但是 f 不为 null,那么不会提交后面的任务,直接返回当前f 的返回值
finally
finally {
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
因为 invokeAny() 只返回一个任务的返回值,所以调用 Future.cancle() 来取消其他任务的返回值
3.4 invokeAll()
try {
for (Callable<T> t : tasks) {
RunnableFuture<T> f = newTaskFor(t);
futures.add(f);
execute(f);
}
for (int i = 0, size = futures.size(); i < size; i++) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
try {
f.get();
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
}
}
}
done = true;
return futures;
} finally {
if (!done)
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
invokeAll() 代码实际上很简单
- 将每一个任务都转换为 RunnableFuture 类型,然后调用 execute()
- 遍历每一个任务的 Future ,如果 future.isDone 为 false,即任务未完成,就调用 Future.get() 来等待该任务完成,该 get() 是一个阻塞方法
- 如果所有的任务都完成了,就设置 done 为 true,然后返回一个 Future 的集合
- 如果在上面的过程中有任何的异常,并且 done 为 false,那么在 finally 中,就会调用Future.cancle()
4. 总结
在上面的博客中,我们分别分析了 Executor,ExecutorService,AbstractExecutorService,这里我做一个总结
-
Executor 接口是最高级的接口,所有关于线程池的类或接口都继承一个接口
内部只有一个方法:execute(),只接收 Runnable 对象
-
ExecutorService 接口为 Executor 增加了几个功能,如:关闭线程池,查看线程池状态,接收 Callable 并可以得到返回值
-
AbstractExcutorService 类是对 ExecutorService 接口的具体实现,主要是对提交任务方法的实现,如:submit(),invokeAll(),invokeAny()