java并发:获取线程执行结果(Callable、Future、FutureTask)
初识Callable and Future
在编码时,我们可以通过继承Thread或是实现Runnable接口来创建线程,但是这两种方式都存在一个缺陷:在执行完任务之后无法获取执行结果。如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到目的。
Java5提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
Callable and Future源码
(1)Callable接口
public interface Callable<V> { V call() throws Exception; }
(2)Future接口
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
源码解说:
(1)Callable位于java.util.concurrent包下,它是一个接口,在它里面只声明了一个call()方法。从上面的源码可以看到,Callable是一个泛型接口,call()函数返回的类型就是传递进来的泛型实参类型。
(2)Future类位于java.util.concurrent包下,Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果,其cancel()方法的参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置为true,则表示可以取消正在执行过程中的任务;get()方法用来获取执行结果,该方法会阻塞直到任务返回结果。
补充:
cancel ⽅法是试图取消⼀个线程的执⾏,并不⼀定能取消成功;因为任务可能已完成、已取消、或者⼀些 其它因素不能取消,存在取消失败的可能。
Callable and Future示例
(1)下面的示例是一个Callable,它会采用最明显的方式查找数组的一个分段中的最大值。
import java.util.concurrent.Callable; class FindMaxTask implements Callable<Integer> { private int[] data; private int start; private int end; FindMaxTask(int[] data, int start, int end) { this.data = data; this.start = start; this.end = end; } public Integer call() { int max = Integer.MIN_VALUE; for (int i = start; i < end; i++) { if (data[i] > max) max = data[i]; } return max; } }
(2)将Callable对象提交给一个Executor,它会为每个Callable对象创建一个线程,如下代码段所示:
import java.util.concurrent.*; public class MultithreadedMaxFinder { public static int max(int[] data) throws InterruptedException, ExecutionException { if (data.length == 1) { return data[0]; } else if (data.length == 0) { throw new IllegalArgumentException(); } // split the job into 2 pieces FindMaxTask task1 = new FindMaxTask(data, 0, data.length/2); FindMaxTask task2 = new FindMaxTask(data, data.length/2, data.length); // spawn 2 threads ExecutorService service = Executors.newFixedThreadPool(2); Future<Integer> future1 = service.submit(task1); Future<Integer> future2 = service.submit(task2); return Math.max(future1.get(), future2.get()); } }
补充:
ExecutorService接口中声明了若干个不同形式的submit()方法,各个方法的返回类型为Future类型,如下:
<T> Future<T> submit(Callable<T> task); <T> Future<T> submit(Runnable task, T result); Future<?> submit(Runnable task);
初识FutureTask
因为Future只是一个接口,所以是无法直接用来创建对象来使用的,因此就有了FutureTask。
FutureTask目前是Future接口的唯一实现类,FutureTask表示一个可以取消的异步运算,它有启动和取消运算、查询运算是否完成和取回运算结果等方法;只有当运算完成的时候才能取回结果,如果运算尚未完成,则get方法将会阻塞。
FutureTask作为线程安全的一次性任务容器,其设计融合了任务封装、状态隔离、并发控制三大核心能力。

接口定义
FutureTask实现了RunnableFuture接口,其声明如下:
public class FutureTask<V> implements RunnableFuture<V>
RunnableFuture接口定义如下:
public interface RunnableFuture<V> extends Runnable, Future<V> { void run(); }
解说:
因RunnableFuture接口继承Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口,所以FutureTask可以通过如下两种方式运行以获取结果:
- 作为Runnable被线程执行(Thread接收Runnable类型的参数)
- 提交给Executor执行(ExecutorService.submit(Runnable task))
FutureTask构造函数
FutureTask的构造函数接收不同形式的参数,如下:
/** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Callable}. * * @param callable the callable task * @throws NullPointerException if the callable is null */ public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable } /** * Creates a {@code FutureTask} that will, upon running, execute the * given {@code Runnable}, and arrange that {@code get} will return the * given result on successful completion. * * @param runnable the runnable task * @param result the result to return on successful completion. If * you don't need a particular result, consider using * constructions of the form: * {@code Future<?> f = new FutureTask<Void>(runnable, null)} * @throws NullPointerException if the runnable is null */ public FutureTask(Runnable runnable, V result) { this.callable = Executors.callable(runnable, result); this.state = NEW; // ensure visibility of callable }
简单示例:
// 封装 Callable FutureTask<String> task = new FutureTask<>(() -> { return "计算结果"; }); // 封装 Runnable(通过 result 传递结果) FutureTask<String> task = new FutureTask<>(() -> { System.out.println("执行任务"); }, "固定结果");
任务状态
FutureTask 的内部有一个变量 state 用来表示任务的状态,相关定义如下:
/** * The run state of this task, initially NEW. The run state * transitions to a terminal state only in methods set, * setException, and cancel. During completion, state may take on * transient values of COMPLETING (while outcome is being set) or * INTERRUPTING (only while interrupting the runner to satisfy a * cancel(true)). Transitions from these intermediate to final * states use cheaper ordered/lazy writes because values are unique * and cannot be further modified. * * Possible state transitions: * NEW -> COMPLETING -> NORMAL * NEW -> COMPLETING -> EXCEPTIONAL * NEW -> CANCELLED * NEW -> INTERRUPTING -> INTERRUPTED */ 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;
解释:
FutureTask 通过 volatile 变量 state 记录任务状态,所有操作均围绕状态流转展开
状态机设计确保状态单向迁移(如 NEW → COMPLETING),无法回退
状态流转规则:
仅 NEW → COMPLETING → NORMAL/EXCEPTIONAL 表示任务执行成功
状态一旦离开 NEW,任务便不可能再次执行(参见后文FeatureTask原理run方法)
FutureTask示例
观察下述两个示例代码中FutureTask的使用方式
示例一
FutureTask将被作为Runnable被线程执行
(1)任务线程ThreadC:
package demo.thread; import java.util.concurrent.Callable; //实现Callable接口,call()方法可以有返回结果 public class ThreadC implements Callable<String> { @Override public String call() throws Exception { try {//模拟任务,执行了500毫秒; Thread.sleep(500L); } catch (InterruptedException e) { e.printStackTrace(); } return "thread B"; } }
(2)主线程ThreadMain:
package demo.thread; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; public class ThreadMain { public static void main(String[] args) { FutureTask<String> feature = new FutureTask<String>(new ThreadC()); new Thread(feature).start();//注意启动方式,FutureTask将被作为Runnable被线程执行 System.out.println("这是主线程;begin!"); //注意细细体会这个,只有主线程get了,主线程才会继续往下执行 try { System.out.println("得到的返回结果是:"+feature.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("这是主线程;end!"); } }
示例二
FutureTask被提交给Executor执行以得到返回值
public class Task implements Callable<Integer>{ @Override public Integer call() throws Exception { Thread.sleep(3*1000); int sum = 0; for(int i=0;i<100;i++) sum += i; return sum; } }
public class Test { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); FutureTask<Integer> futureTask = new FutureTask<Integer>(new Task()); executor.submit(futureTask);//FutureTask被提交给Executor执行以得到返回值 executor.shutdown(); try { Thread.sleep(1000); } catch (InterruptedException e1) { e1.printStackTrace(); } System.out.println("主线程在执行任务"); try { System.out.println("task运行结果"+futureTask.get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.println("所有任务执行完毕"); } }
FutureTask原理
FutureTask 能在高并发环境下确保任务仅执行一次,其关键在于内部实现了线程安全的状态机和原子性控制机制。
典型应用场景
class AtomicCache<K, V> { private final Map<K, FutureTask<V>> cache = new ConcurrentHashMap<>(); public V get(K key) throws ExecutionException { FutureTask<V> task = cache.computeIfAbsent(key, k -> new FutureTask<>(() -> computeExpensively(k))); task.run(); // 确保并发下仅执行一次计算 return task.get(); } }
高并发场景下的行为
1. 首次执行成功
线程A:通过 CAS 成为 runner → 执行任务 → 更新状态为 NORMAL
线程B:检测到 state != NEW → 直接返回
2. 执行中取消任务
线程A:正在执行任务
线程B:调用 cancel(true) → 通过 CAS 将状态改为 INTERRUPTING
线程A:检测到状态变化 → 响应中断 → 最终状态变为 INTERRUPTED
3. 重复调用 run()
若多个线程同时调用 run():
仅一个线程通过 CAS 成为 runner
其余线程因状态检查失败或 CAS 竞争失败而退出
并发控制:CAS + 自旋锁
任务执行入口(run()方法)
public void run() { if (state != NEW || // 状态检查 !RUNNER.compareAndSet(this, null, Thread.currentThread())) // CAS抢占执行权 return; try { Callable<V> c = callable; if (c != null && state == NEW) { // 二次状态检查(Double-Check) V result; boolean ran; try { result = c.call(); // 实际执行任务 ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); // 异常状态转换 } if (ran) set(result); // 正常状态转换 } } finally { runner = null; // 释放执行线程引用 if (state == INTERRUPTING) // 处理中断 handlePossibleCancellationInterrupt(); } }
解读:
非NEW状态直接退出
关键保护点:
CAS 抢占:通过 compareAndSet 确保仅一个线程成为执行者(runner)
状态双重检查:防止执行权抢占后状态被其他线程修改
状态变更的原子性
无锁化状态跃迁:状态转换通过 UNSAFE.compareAndSwapInt 实现原子操作
set()方法
protected void set(V v) { if (STATE.compareAndSet(this, NEW, COMPLETING)) { // 原子状态跃迁 outcome = v; STATE.setRelease(this, NORMAL); // 最终状态,内存语义:写后可见 finishCompletion(); // 唤醒等待线程 } }
解读:
任务结果(outcome)一旦设置,即被**final 语义保护** —— 非volatile,依赖状态机保证可见性
任务完成后调用 finishCompletion(),自旋唤醒所有等待线程
get()方法
/** * @throws CancellationException {@inheritDoc} */ public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); } /** * @throws CancellationException {@inheritDoc} */ public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if (unit == null) throw new NullPointerException(); int s = state; if (s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING) throw new TimeoutException(); return report(s); }
/** * Awaits completion or aborts on interrupt or timeout. * * @param timed true if use timed waits * @param nanos time to wait, if timed * @return state upon completion */ private int awaitDone(boolean timed, long nanos) throws InterruptedException { final long deadline = timed ? System.nanoTime() + nanos : 0L; WaitNode q = null; boolean queued = false; for (;;) { if (Thread.interrupted()) { removeWaiter(q); throw new InterruptedException(); } int s = state; if (s > COMPLETING) { if (q != null) q.thread = null; return s; } else if (s == COMPLETING) // cannot time out yet Thread.yield(); else if (q == null) q = new WaitNode(); // 延迟初始化 else if (!queued) queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q); // CAS入队 else if (timed) { nanos = deadline - System.nanoTime(); if (nanos <= 0L) { removeWaiter(q); return state; } LockSupport.parkNanos(this, nanos); } else LockSupport.park(this); // 最终挂起 } }
解读:自旋优化 —— 短时自旋避免立即挂起线程
/** * Returns result or throws exception for completed task. * * @param s completed state value */ @SuppressWarnings("unchecked") private V report(int s) throws ExecutionException { Object x = outcome; if (s == NORMAL) return (V)x; if (s >= CANCELLED) throw new CancellationException(); throw new ExecutionException((Throwable)x); }
等待机制:
未完成的任务会构建一个 WaitNode 链表(通过 CAS 追加节点)
FutureTask小结
与同步原语的性能对比

设计模式
前面提到了一条重要信息:ExecutorService接口中的submit()方法可以接收callable、Runnable类型的参数,方法的返回类型为Future类型。
ExecutorService的submit()方法的内部实现是根据参数构建了FutureTask对象,然后将FutureTask对象转为Future类型返回。
这也对应了下面这条信息:
FutureTask间接继承了Future接口,其构造函数可以接收callable、Runnable类型的参数。
Note:
仔细想一想,其实这个内部实现使用了适配器模式,使得不同接口的实现最终对外表现为一致

浙公网安备 33010602011771号