1. 写在前面
ExecutorService 是一个非常重要的接口,用于管理和调度线程池。在日常工作中我们应该也是用的比较多,但是你真正了解它的底层实现原理吗?下面的几个问题在日常开发中你是否思考过?面试中是否被问到过?
- 什么是 ExecutorService?它与 Executor 有什么区别?
- 如何优雅地关闭 ExecutorService?
- 如何提交一个带返回值的任务?
- 如何处理任务执行中的异常?
- 什么是 invokeAll 和 invokeAny 方法?
- 如何创建一个自定义线程池?
- 什么是 ScheduledExecutorService?如何使用它调度任务?
- 如何在 ExecutorService 中处理大量短时间任务?
2. 全局视角
2.1 Executor 接口
ExecutorService 继承自 Executor 接口,该接口是 Java 并发包中最基础的接口之一。它定义了一个方法:
public interface Executor {
void execute(Runnable command);
}
2.2 ExecutorService 接口
ExecutorService 扩展了 Executor 接口,增加了更多用于管理任务和线程池生命周期的方法:
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;
}
这些方法后面我们会展开介绍。
2.3 具体实现类
ExecutorService 有多个具体的实现类,分别适用于不同的场景:
2.3.1 ThreadPoolExecutor
- 最常用的实现类,可以通过多种方式配置线程池的行为。
- 直接或间接通过 Executors 工具类创建,如 Executors.newFixedThreadPool(),Executors.newCachedThreadPool() 等。
2.3.2 ScheduledThreadPoolExecutor
- 扩展自 ThreadPoolExecutor,实现了 ScheduledExecutorService 接口,支持任务调度。
- 可以通过 Executors.newScheduledThreadPool() 创建。
2.3.3 ForkJoinPool
- 实现了 ExecutorService 和 ForkJoinPool 接口,适用于分治任务。
- 主要用于并行计算,支持工作窃取算法。
3. submit()方法的底层实现
ExecutorService 接口的 submit 方法用于提交任务并返回一个 Future 对象,通过这个 Future 对象可以获取任务的执行结果或取消任务。Java 8中的 ExecutorService 接口有多个实现类,其中最常用的是 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor。
submit接口 不同的实现类有不同的实现,如下图所示:
3.1 ThreadPoolExecutor 的 submit 方法实现
ThreadPoolExecutor 是 ExecutorService 的核心实现类,提供了灵活的线程池配置。它继承自 AbstractExecutorService,并使用该抽象类提供的 submit 方法的默认实现。
AbstractExecutorService 提供了 submit 方法的默认实现:
public abstract class AbstractExecutorService implements ExecutorService {
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
}
- 参数检查
检查传入的任务是否为 null,如果是则抛出 NullPointerException。 - 创建 RunnableFuture
使用 newTaskFor 方法将 Runnable 或 Callable 任务包 - 执行任务
调用 execute 方法将 RunnableFuture 提交给线程池执行。 - 返回 Future
返回 RunnableFuture 对象,该对象同时实现了 Future 接口,可以用于获取任务的结果或取消任务。
3.2 ScheduledThreadPoolExecutor 的 submit 方法实现
ScheduledThreadPoolExecutor 继承自 ThreadPoolExecutor,并实现了 ScheduledExecutorService 接口。它提供了调度任务的功能,但对于 submit 方法,它直接继承了 ThreadPoolExecutor 的实现。
ScheduledThreadPoolExecutor 类
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
// 省略了调度任务相关的方法
// submit 方法直接继承自 ThreadPoolExecutor
}
FutureTask 类
FutureTask 是 RunnableFuture 的一个实现类,主要用于包装 Runnable 和 Callable 任务。它的构造函数如下:
public class FutureTask<V> implements RunnableFuture<V> {
public FutureTask(Callable<V> callable) {
// 初始化
}
public FutureTask(Runnable runnable, V result) {
// 初始化
}
// 实现了 Runnable 和 Future 的方法
}
4. invokeAll()的底层实现
ExecutorService 接口的 invokeAll 方法用于批量提交一组 Callable 任务,并返回一个 Future 列表,通过这些 Future 对象可以获取每个任务的执行结果。
4.1 ExecutorService 接口中的 invokeAll 方法
ExecutorService 接口定义了两个 invokeAll 方法:
<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;
4.2 AbstractExecutorService 中的 invokeAll 方法
AbstractExecutorService 提供了 invokeAll 方法的默认实现:
public abstract class AbstractExecutorService implements ExecutorService {
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
if (tasks == null) throw new NullPointerException();
ArrayList<Future<T>> futures = new ArrayList<>(tasks.size());
boolean done = false;
try {
for (Callable<T> task : tasks) {
RunnableFuture<T> f = newTaskFor(task);
futures.add(f);
execute(f);
}
for (Future<T> f : futures) {
if (!f.isDone()) {
try {
f.get();
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
}
}
}
done = true;
return futures;
} finally {
if (!done) {
for (Future<T> f : futures) {
f.cancel(true);
}
}
}
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException {
if (tasks == null || unit == null) throw new NullPointerException();
long nanos = unit.toNanos(timeout);
ArrayList<Future<T>> futures = new ArrayList<>(tasks.size());
boolean done = false;
try {
for (Callable<T> task : tasks) {
futures.add(newTaskFor(task));
}
final long deadline = System.nanoTime() + nanos;
for (Future<T> f : futures) {
execute((Runnable) f);
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
return futures;
}
}
for (Future<T> f : futures) {
if (!f.isDone()) {
if (nanos <= 0L) {
return futures;
}
try {
f.get(nanos, TimeUnit.NANOSECONDS);
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
} catch (TimeoutException toe) {
return futures;
}
nanos = deadline - System.nanoTime();
}
}
done = true;
return futures;
} finally {
if (!done) {
for (Future<T> f : futures) {
f.cancel(true);
}
}
}
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
}
invokeAll(Collection<? extends Callable> tasks) 主要步骤介绍:
4.2.1 参数检查
检查传入的任务集合是否为 null,如果是则抛出 NullPointerException。
4.2.2 任务初始化
- 创建一个 ArrayList 来存储 Future 对象。
- 遍历任务集合,将每个 Callable 任务包装成 RunnableFuture 对象,并将其添加到 futures 列表中。
4.2.3 任务提交
调用 execute 方法将每个 RunnableFuture 提交给线程池执行
4.2.4 等待任务完成
遍历 futures 列表,调用 Future.get() 方法等待每个任务完成。如果任务被取消或抛出异常,则忽略这些异常。
4.2.5 异常处理和任务取消
如果在等待任务完成期间抛出异常,则取消所有未完成的任务
4.3 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 的 invokeAll 方法实现
ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 直接继承了 AbstractExecutorService 中的 invokeAll 方法,因此它们的 invokeAll 方法实现逻辑与 AbstractExecutorService 中的实现相同。
5. invokeAny()的底层实现
ExecutorService 接口的 invokeAny 方法用于批量提交一组 Callable 任务,并返回其中一个成功完成的任务的结果。与 invokeAll 不同的是,invokeAny 只需要等待其中一个任务成功完成即可。
5.1 ExecutorService 接口中的 invokeAny 方法
ExecutorService 接口定义了两个 invokeAny 方法:
<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;
5.2 AbstractExecutorService 中的 invokeAny 方法
AbstractExecutorService 提供了 invokeAny 方法的默认实现:
public abstract class AbstractExecutorService implements ExecutorService {
public <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException {
try {
return doInvokeAny(tasks, false, 0);
} catch (TimeoutException cannotHappen) {
assert false;
return null;
}
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return doInvokeAny(tasks, true, unit.toNanos(timeout));
}
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks, boolean timed, long nanos) throws InterruptedException, ExecutionException, TimeoutException {
if (tasks == null) throw new NullPointerException();
int ntasks = tasks.size();
if (ntasks == 0) throw new IllegalArgumentException();
ArrayList<Future<T>> futures = new ArrayList<>(ntasks);
ExecutorCompletionService<T> ecs = new ExecutorCompletionService<>(this);
try {
ExecutionException ee = null;
final long deadline = timed ? System.nanoTime() + nanos : 0L;
Iterator<? extends Callable<T>> it = tasks.iterator();
futures.add(ecs.submit(it.next()));
--ntasks;
int active = 1;
for (;;) {
Future<T> f = ecs.poll();
if (f == null) {
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next()));
++active;
} else if (active == 0)
break;
else if (timed) {
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null)
throw new TimeoutException();
nanos = deadline - System.nanoTime();
} else
f = ecs.take();
}
if (f != null) {
--active;
try {
return f.get();
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
}
if (ee == null)
ee = new ExecutionException();
throw ee;
} finally {
for (Future<T> f : futures)
f.cancel(true);
}
}
}
invokeAny(Collection<? extends Callable> tasks)主要步骤解析
- 参数检查
- 检查传入的任务集合是否为 null,如果是则抛出 NullPointerException。
- 检查任务集合是否为空,如果是则抛出 IllegalArgumentException。
- 任务提交和执行
- 创建一个 ExecutorCompletionService 实例,用于管理任务的提交和结果的获取。
- 提交第一个任务,并将其 Future 对象添加到 futures 列表中。
- 使用循环依次提交剩余的任务,并在 ExecutorCompletionService 中等待第一个完成的任务。
- 任务结果获取
- 使用 ecs.poll() 或 ecs.take() 方法获取已完成的任务。
- 如果任务成功完成,则返回其结果。
- 如果任务抛出异常,则记录异常并继续等待其他任务完成。
- 异常处理和任务取消
- 如果所有任务都抛出异常,则抛出最后一个异常
- 在方法结束时,取消所有未完成的任务
invokeAny(Collection<? extends Callable> tasks, long timeout, TimeUnit unit)主要步骤解析
- 参数检查
- 检查传入的任务集合和时间单位是否为 null,如果是则抛出 NullPointerException。
- 检查任务集合是否为空,如果是则抛出 IllegalArgumentException。
- 任务提交和执行
- 与无超时版本相同,创建一个 ExecutorCompletionService 实例,并提交第一个任务。
- 计算截止时间 deadline。
- 任务结果获取
- 使用 ecs.poll(nanos, TimeUnit.NANOSECONDS) 方法在指定时间内等待任务完成。
- 如果任务在超时时间内完成,则返回其结果。
- 如果超时,则抛出 TimeoutException。
- 异常处理和任务取消
无超时版本相同,处理任务抛出的异常,并在方法结束时取消所有未完成的任务。
5.3 ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 的 invokeAny 方法实现
ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 直接继承了 AbstractExecutorService 中的 invokeAny 方法,因此它们的 invokeAny 方法实现逻辑与 AbstractExecutorService 中的实现相同。
系列文章
7.jdk源码阅读之ConcurrentHashMap(上)
8.jdk源码阅读之ConcurrentHashMap(下)
17.jdk源码阅读之LinkedBlockingQueue