前文线程池执行器只讲了 Executor.execute() 部分, execute() 方法只能接受 Runnable 且无返回值,下面就接着讲一下 ExecutorService 拓展的有返回值的 submit 方法。
| 方法签名 | 说明 | 注意点 |
|---|---|---|
Future<?> submit(Runnable task) |
提交 Runnable 任务,无返回值 | Future.get () 成功返回 null;任务异常封装进 Future,get 才抛出 |
<T> Future<T> submit(Runnable task, T result) |
提交 Runnable,任务完成后返回传入的 result | 适合需要固定返回值场景 |
<T> Future<T> submit(Callable<T> task) |
提交带返回值的 Callable 任务 | 支持获取任务执行结果 |
基本使用
java
ExecutorService pool = Executors.newFixedThreadPool(2);
// 1. Callable 版本(最常用,有返回)
Future<Integer> future1 = pool.submit(() -> {
Thread.sleep(1000);
return 100 + 200;
});
// 获取结果,阻塞等待任务完成
Integer res = future1.get();
System.out.println(res);
// 2. Runnable + result 版本
Future<String> future2 = pool.submit(() -> {
System.out.println("runnable任务执行");
}, "任务完成标识");
String str = future2.get();
System.out.println(str); // 输出 "任务完成标识"
pool.shutdown();
前文介绍的 Executor.execute(Runnable) 方法有一个明显的局限------它只能接受 Runnable 任务,而 Runnable 的 run() 方法没有返回值,且线程内部抛出的异常无法被外部捕获。
实际开发中,我们常常需要:
- 任务执行完后返回一个结果
- 任务执行过程中抛出异常时能够感知
- 能够主动取消正在执行的任务
ExecutorService 接口在 Executor 的基础上拓展了 submit() 方法,就是为了解决上述问题的。
submit() 方法,本质上符合 Future 模式 ,也可以叫 期货模式。
期货模式 Future
Future (期货)模式:异步处理任务并在未来获取结果,就是 sumbit 提交一个耗时任务,会立刻返回一个 Future "提货单",后台线程继续跑任务;主线程先做别的事,等到真正需要结果时,再凭 Future 去拿结果。
可以想一下,如果要想实现这个功能需要怎么搞?
- 客户端 向线程池提交任务
- 线程池 收到任务,新开线程异步跑这个任务,然后直接返回一个 Future "提货单"。
- 客户端通过这个Future "提货单",可以
get到任务执行完的结果或异常。 - 如果任务还没执行完毕,get 任务的时候需要阻塞等待,任务执行完自动唤醒。
梳理完步骤,实现这个设计模式需要几个角色大致就有数了:
⨳ Client 客户端 :调用 submit,提交耗时任务,拿到返回的 Future 提货单,然后可以执行其他业务代码,后续调用 future.get() 获取结果,没完成就阻塞。
⨳ Task 任务 :封装需要异步执行的耗时逻辑,任务执行后,产出返回结果。
⨳ Future 提货单 :需要对外暴露 阻塞获取结果的方法 get,需要感知任务执行状态,是否已完成,还需要有线程等待-通知机制,实现阻塞唤醒。
⨳ Executor 任务执行者 :接收客户端提交的任务,启动子线程,异步执行任务,给客户端返回 Future 提货单 ,任务执行完毕后,把结果填入 Future(提货单) ,唤醒正在get()等待的客户端线程。
描述清楚了,代码就好实现了。
任务执行者 Executor
java
import java.util.concurrent.Callable;
public class MyExecutor {
public <V> MyFuture<V> submit(Callable<V> task) {
MyFuture<V> future = new MyFuture<>();
new Thread(() -> {
try {
V res = task.call();
future.setResult(res);
} catch (Exception e) {
future.setException(e);
}
}).start();
return future;
}
}
提货单 Future
java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class MyFuture<V> {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private V result;
private Throwable exception;
private boolean done = false;
// 阻塞获取结果,await代替wait
public V get() throws InterruptedException {
lock.lock();
try {
// while防止虚假唤醒
while (!done) {
condition.await();
}
if (exception != null) {
throw new RuntimeException(exception);
}
return result;
} finally {
lock.unlock(); // finally保证锁一定释放
}
}
// 任务成功,回填结果,唤醒等待线程
public void setResult(V result) {
lock.lock();
try {
this.result = result;
this.done = true;
condition.signalAll();
} finally {
lock.unlock();
}
}
// 任务异常,回填异常
public void setException(Throwable e) {
lock.lock();
try {
this.exception = e;
this.done = true;
condition.signalAll();
} finally {
lock.unlock();
}
}
}
客户端 Client
java
public class Client {
public static void main(String[] args) throws InterruptedException {
MyExecutor executor = new MyExecutor();
System.out.println("客户端:提交耗时任务");
MyFuture<String> future = executor.submit(() -> {
Thread.sleep(2500);
return "任务结果:商品数据";
});
System.out.println("拿到Future提货单,主线程去做别的工作");
Thread.sleep(1000);
System.out.println("主线程其他工作完成,准备获取结果:");
long beginDate = System.currentTimeMillis();
System.out.println("获取开始时间:"+beginDate);
String data = future.get();
long endDate = System.currentTimeMillis();
System.out.println("获取结束时间:"+endDate);
System.out.println("拿到结果:" + data+";获取耗时:"+(endDate-beginDate));
}
}
输出结果如下:
java
客户端:提交耗时任务
拿到Future提货单,主线程去做别的工作
主线程其他工作完成,准备获取结果:
获取开始时间:1792032090239
获取结束时间:1792032091747
拿到结果:任务结果:商品数据;获取耗时:1508
可以把 Future,看作是守护任务结果 的同部锁,守护条件是存在任务结果,守护条件不满足则 get 阻塞。
下面看看 JDK 是怎么实现 Future 期货模式的。
Future 继承体系

Future
⨳ Future :异步结果的 顶层抽象接口,代表一个还没算出来的结果。
| 方法 | 说明 |
|---|---|
V get() |
阻塞获取结果;任务没跑完就一直阻塞等待。任务完成后返回结果 |
V get(long timeout, TimeUnit unit) |
带超时的阻塞获取。最多等待指定时长,拿不到结果直接抛异常 |
boolean cancel(boolean mayInterruptIfRunning) |
尝试取消任务。任务还没开始:直接取消,返回 true;任务正在运行,mayInterruptIfRunning=true,尝试中断运行线程;false 则不中断,让它跑完 |
boolean isCancelled() |
任务在完成之前被取消,返回 true;正常跑完返回 false |
boolean isDone() |
任务是否结束:正常完成、抛异常、被取消,全部返回 true,不是只有成功才算 done |
可以通过这个 "提货单" 查询任务是否完成、阻塞等待结果、尝试取消任务。
RunnableFuture
⨳ RunnableFuture :是一个桥接接口,就是把一个"可执行的任务"和一个"可获取结果的 Future"合并到一起。
java
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
它只是继承并重写了run(),没有新增别的方法。
Thread 只接受 Runnable,但它没有返回值,Future 能拿结果,但它们都不能被线程执行,所以需要 既继承 Runnable,又继承 Future 的RunnableFuture,既能被线程执行,又能提供结果访问能力。
FutureTask
⨳ FutureTask :是 RunnableFuture 的标准实现。
java
public class FutureTask<V> implements RunnableFuture<V> {
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
/** The thread running the callable; CASed during run() */
private volatile Thread runner;
/** Treiber stack of waiting threads */
private volatile WaitNode waiters;
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
它是 JDK 线程池 submit() 底层包装任务的核心类,既能交给线程/线程池执行,又能通过 get() 拿结果。
state 语义
先看 state 变量,这是任务状态机。
| 状态 | 值 | 含义 |
|---|---|---|
NEW |
0 | 新建,任务还没开始执行 |
COMPLETING |
1 | 过渡态:业务执行完,正在把结果/异常写入outcome |
NORMAL |
2 | 终态:任务正常执行成功,outcome 存返回值 |
EXCEPTIONAL |
3 | 终态:任务抛出异常,outcome存Throwable |
CANCELLED |
4 | 终态:cancel(false),任务未启动,不中断线程 |
INTERRUPTING |
5 | 过渡态:cancel(true),准备中断runner线程 |
INTERRUPTED |
6 | 终态:cancel(true),已经中断执行线程 |
只有 NEW 是初始状态;COMPLETING / INTERRUPTING 是短暂中间过渡态,很快会流转到终态,终态一旦进入,永不回退。
所有判断任务是否完成、能否取消,都是读这个 volatile 修饰的 state。
注意,对于保存结果的 outcome 并没有由 volatile 修饰。这说明对 outcome 的写应该发生在 对 state 的写之前,对 outcome 的读,应该发生在对 state 的读之后。
Callable 需执行任务
Callable 不能直接交给 Thread 执行的,而是用来描述"一个有返回值的异步任务"的,属于任务本身的"业务定义"。
线程池 submit 既能提交 Callable ,又能提交 Runnable,看 FutureTask 构造方法就知道,无论是 Callable 还是 Runnable 都会被包装成 Callable。
那没有返回值的 Runnable,怎么才能变成有返回值的 Callable 呢?
java
public class Executors {
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
private static final class RunnableAdapter<T> implements Callable<T> {
private final Runnable task;
private final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
public String toString() {
return super.toString() + "[Wrapped task = " + task + "]";
}
}
原来是一个包装 Runnable 的适配器,把 Runnable.run() 包进 call() 方法里。 run() 没有返回,所以可以预先 传一个 result 对象,只要 run 不抛异常,call() 就原样把这个 result 返回。
runner 执行任务的线程
Thread runner 表示当前执行任务的线程。直接看 FutureTask 的 run 方法。
java
public void run() {
// 1. 检查状态是否为 NEW,并用 CAS 将当前线程设为 runner,保证只有一个线程能执行任务
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
// 2. 本地缓存 callable,避免重复读取 volatile 字段
Callable<V> c = callable;
// 3. 再次确认 callable 不为 null 且任务仍处于 NEW 状态,防止被取消或重复执行
if (c != null && state == NEW) {
V result; // 任务返回值
boolean ran; // 任务是否成功执行
try {
// 4. 真正执行业务逻辑
result = c.call();
ran = true; // 标记执行成功
} catch (Throwable ex) {
// 5. 执行异常,保存异常信息
result = null;
ran = false;
setException(ex); // 将状态置为 EXCEPTIONAL,outcome 保存异常
}
// 6. 如果执行成功,将结果写入 outcome,状态置为 NORMAL
if (ran)
set(result);
}
} finally {
// 7. 执行完成后清空 runner,防止其他线程误判
// runner 必须在状态稳定后才置 null,避免并发调用 run()
runner = null;
// 8. 重新读取状态,防止在 cancel(true) 中断后出现中断泄漏
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
嗷嗷嗷,只有 runner 为 null 的时候,才能把当前线程 CAS 设置成执行线程,防止多个线程并发执行同一个 FutureTask。
除了标记这个任务有人认领了之外,其他线程还能拿到这个线程去 interrupt。
java
public boolean cancel(boolean mayInterruptIfRunning) {
/*
* 第一步:CAS修改任务状态
* 前提条件:当前state必须是NEW(任务还未开始执行)
* mayInterruptIfRunning=true:NEW → INTERRUPTING(中间过渡态,准备中断线程)
* mayInterruptIfRunning=false:NEW → CANCELLED(终态,直接标记取消,不碰执行线程)
* CAS失败(state不是NEW,或者CAS竞争失败),直接返回false,取消失败
*/
if (!(state == NEW && STATE.compareAndSet
(this, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try {
// 进入这里代表CAS状态修改成功,任务进入取消流程
// 捕获interrupt可能抛出的异常,保证后续finishCompletion一定执行
if (mayInterruptIfRunning) {
try {
// 获取runner保存的正在执行任务的线程引用
Thread t = runner;
if (t != null)
t.interrupt(); // 给执行线程设置中断标志,发送中断信号
} finally {
// 把中间过渡态INTERRUPTING 设置为终态 INTERRUPTED
// setRelease:volatile写,内存屏障,对其他线程可见
STATE.setRelease(this, INTERRUPTED);
}
}
} finally {
/*
* 无论是否执行中断,最终都执行finishCompletion
* 作用:唤醒所有阻塞在get()等待结果的线程,清理waiters等待链表
* get()的线程被唤醒后,读取state,发现任务被取消,抛出CancellationException
*/
finishCompletion();
}
// 取消成功
return true;
}
取消的前提 state 必须是 NEW,正常完成、异常、已经取消,cancel 直接返回 false,无法取消。
mayInterruptIfRunning = false,NEW 直接进入 CANCELLED 终态,只是标记任务取消,正在运行的线程会继续跑完,不会被打断。
mayInterruptIfRunning = true,把 NEW 更新成 INTERRUPTING 中间态,然后 获取 runner 线程调用interrupt() ,t.interrupt() 只是设置中断标记,也不是强行杀死线程,finally 将状态改为 INTERRUPTED 终态。
js
NEW ──成功执行──→ COMPLETING → NORMAL
│ → get() 返回结果
├──执行异常──→ COMPLETING → EXCEPTIONAL
│ → get() 抛 ExecutionException
├──cancel(false)───────────→ CANCELLED
│ → get() 抛 CancellationException
└──cancel(true)──→ INTERRUPTING → INTERRUPTED
→ get() 抛 CancellationException
waiters 阻塞的线程
WaitNode waiters 是 Treiber Stack(无锁并发栈,CAS 头插) 的栈顶指针。保存所有调用 future.get() 阻塞等待结果的线程:
java
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
当调用future.get(),任务还没完成(state <= COMPLETING):
- 当前线程新建一个
WaitNode - 使用 CAS 把新节点插到栈头(修改
waiters) - 然后
LockSupport.park(this)阻塞当前线程,挂起等待任务完成
多个线程并发调用get(),会不断把 WaitNode 头插到栈里。直到务正常完成 / 抛异常 /cancel 取消成功 ,都会调用 finishCompletion():
java
private void finishCompletion() {
// 遍历waiters栈,逐个唤醒等待的线程
for (WaitNode q; (q = waiters) != null; ) {
// CAS把栈顶置null,尝试清空整个栈
if (WAITERS.weakCompareAndSet(this, q, null)) {
for (; ; ) {
Thread t = q.thread;
if (t != null) {
q.thread = null; // 帮助GC
LockSupport.unpark(t); // 唤醒被park阻塞的线程
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // 断开引用,帮助GC
q = next;
}
break;
}
}
callable = null; // 清空callable引用,GC
done(); // 钩子方法,空实现,子类扩展
}
下面就看一下,线程池的 submit 方法,完成逻辑的闭环。
回看线程池
submit 提交
| 方法签名 | 说明 | 注意点 |
|---|---|---|
Future<?> submit(Runnable task) |
提交 Runnable 任务,无返回值 | Future.get () 成功返回 null;任务异常封装进 Future,get 才抛出 |
<T> Future<T> submit(Runnable task, T result) |
提交 Runnable,任务完成后返回传入的 result | 适合需要固定返回值场景 |
<T> Future<T> submit(Callable<T> task) |
提交带返回值的 Callable 任务 | 支持获取任务执行结果 |
无论传给 submit 的是 Runnable 还是 Callable,都会通过newTaskFor包装成RunnableFuture(实现类就是 FutureTask),再把这个 RunnableFuture 传给 execute 执行。同时 submit 将同一个 RunnableFuture 对象作为 Future 返回给调用线程。
java
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;
}
调用方持有的引用,和线程池 worker 执行的是同一个 FutureTask 实例 。这就是为什么调用方在别的线程调用 future.cancel(),可以修改任务的 state、中断 runner 线程。
将这个 FutureTask 交给 execute 方法,ThreadPoolExecutor 执行 execute 的过程见 前文,简单来说就是:优先创建核心线程 → 任务入队排队 → 队列满才创建非核心线程 → 最后拒绝。
invoke 调用
除了 submit ,ExecutorService 拓展了两个入参是 Callable 的 invoke 方法。
java
// 单个Callable,返回Future,非阻塞提交
<T> Future<T> submit(Callable<T> task);
// 批量:任意一个成功就返回结果,阻塞
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
// 批量:等待全部任务结束,返回Future列表,阻塞
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
invokeAll 很好理解,遍历任务 execute 执行,然后 get 阻塞等待结果就好了:
java
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
// 任务集合不能为null,空集合允许(返回空futures)
if (tasks == null)
throw new NullPointerException();
// 将超时时间统一转为纳秒,计算截止时间点
final long nanos = unit.toNanos(timeout);
final long deadline = System.nanoTime() + nanos;
// 预创建Future容器,newTaskFor把Callable包装成RunnableFuture
ArrayList<Future<T>> futures = new ArrayList<>(tasks.size());
int j = 0; // 记录等待到第几个任务,超时后用于取消后面未完成任务
// 带标签的try,超时后直接跳到末尾的timedOut分支
timedOut: try {
// 遍历所有任务:将Callable包装成RunnableFuture,存入futures列表
// newTaskFor(t) 本质返回 FutureTask,既可以作为Runnable交给execute,又能保存结果/异常
for (Callable<T> t : tasks)
futures.add(newTaskFor(t));
final int size = futures.size();
// 逐个提交任务到线程池执行;穿插时间判断
// 注释原意:如果线程池并行度很低(例如单线程池),边提交边判断时间,防止一次性提交全部任务浪费超时时间
for (int i = 0; i < size; i++) {
// i==0:第一次判断,直接用剩余nanos;后面判断:当前距离截止时间是否<=0
if (((i == 0) ? nanos : deadline - System.nanoTime()) <= 0L)
break timedOut; // 超时,跳出整个timedOut代码块
// execute执行FutureTask(FutureTask实现了Runnable)
execute((Runnable) futures.get(i));
}
// 循环等待每一个任务完成:逐个调用带超时get阻塞获取结果
for (; j < size; j++) {
Future<T> f = futures.get(j);
if (!f.isDone()) { // 任务未结束,尝试限时等待
try {
// 剩余时间 = 截止时间 - 当前时间
f.get(deadline - System.nanoTime(), NANOSECONDS);
} catch (CancellationException | ExecutionException ignore) {
// 任务被取消 / 任务内部抛异常:这两类异常不打断invokeAll,继续等下一个任务
} catch (TimeoutException timedOut) {
// 等待单个任务时超时,直接跳转到timedOut标签
break timedOut;
}
}
}
// 所有任务全部等待完成,返回Future列表
return futures;
} catch (Throwable t) {
// 出现其他异常,取消全部任务再向上抛异常
cancelAll(futures);
throw t;
}
// ========== 走到这里代表【超时】==========
// j:前面j个任务已经处理完毕,从下标j开始,取消剩下未完成的任务
cancelAll(futures, j);
return futures;
}
invokeAny 呢?
java
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();
// 保存所有提交出去的Future,finally里面统一cancelAll
ArrayList<Future<T>> futures = new ArrayList<>(ntasks);
// 核心:ExecutorCompletionService,按【任务完成顺序】获取结果
ExecutorCompletionService<T> ecs = new ExecutorCompletionService<T>(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; // 当前正在运行中的任务计数
// 无限循环:边提交剩余任务、边从ecs获取已完成任务
for (;;) {
// poll() 非阻塞尝试拿【已经完成】的任务,没有返回null
Future<T> f = ecs.poll();
if (f == null) {
// ① 还有任务没提交:继续提交下一个任务
if (ntasks > 0) {
--ntasks;
futures.add(ecs.submit(it.next()));
++active;
}
// ② 没有剩余任务,active=0:所有任务执行完毕,没有成功的,跳出循环抛异常
else if (active == 0) {
break;
}
// ③ 开启了超时模式:带超时的poll阻塞等待完成任务
else if (timed) {
f = ecs.poll(nanos, NANOSECONDS);
if (f == null) {
// 超时,直接抛出TimeoutException
throw new TimeoutException();
}
// 更新剩余超时时间
nanos = deadline - System.nanoTime();
}
// ④ 不开启超时:take() 阻塞,直到有任务完成
else {
f = ecs.take();
}
}
// 拿到了一个【已经完成】的任务Future
if (f != null) {
--active; // 正在运行任务数减一
try {
// 获取结果,成功直接return
return f.get();
} catch (ExecutionException eex) {
// 这个任务执行抛异常,记录异常,继续循环等下一个完成任务
ee = eex;
} catch (RuntimeException rex) {
// 任务抛出运行时异常,包装成ExecutionException
ee = new ExecutionException(rex);
}
}
}
// 循环结束代表全部任务执行完,无任何成功任务,抛出异常
if (ee == null)
ee = new ExecutionException();
throw ee;
} finally {
// 无论正常返回、异常退出,都取消所有已经提交的任务
cancelAll(futures);
}
}
invokeAny 使用了 ExecutorCompletionService 。ECS 虽然是对线程池做的一层包装, 但它内部维护了一个阻塞队列,哪个任务先执行完毕,优先出队。
invokeAny 也不像 invokeAll 不是一次性全部 execute/submit 所有任务,而是先提交 1 个任务,每次循环先 ecs.poll() 看有没有已经跑完的任务,有完成任务 就 先处理结果,没有完成任务 再继续提交下一个任务。
直到获取结果,成功 return,剩下正在执行的任务,全部取消cancelAll(futures),调用 interrupt 尝试中断正在执行线程。
总结
Executor接口的execute()只能提交无返回值 Runnable 任务。ExecutorService扩展的submit方法基于 Future(期货)模式,支持提交带返回值的 Callable 任务,支持任务结果获取、异常捕获与任务取消。
Future 模式很像 金融领域的期货(Futures),所以名字就叫 Future Pattern。
期货合约 是一份标准化的、受交易所监管的法律协议:买卖双方约定,在未来某个确定时间、按约定价格,交割指定标的资产。
合约本身是凭证,代表未来获取商品的权利,不是商品本身:Future 对象就像这份合约,不是最终结果,是未来拿到结果的凭证。
| 金融期货合约 | 并发 Future(期货模式) |
|---|---|
| 合约:未来拿到货物的凭证 | Future 对象:未来拿到计算结果的凭证 |
| 后台:市场到到期日完成交割 | 后台:子线程执行耗时任务,计算结果 |
| 交割:到期拿到标的 | get ():阻塞,获取最终返回值 |
| 可提前平仓转手 | CompletableFuture 可 complete () 手动完成 |
附:UML 类图
uml
@startuml
interface Future<V>{}
interface CompletionStage<T> {}
class CompletableFuture<T> implements Future, CompletionStage {}
interface RunnableFuture<V> extends Runnable, Future{}
class FutureTask<V> implements RunnableFuture {}
abstract class ForkJoinTask<V> implements Future {}
@enduml