futuretask 利用 volatile 修饰内部状态变量 state,确保线程对任务执行状态的修改对其他线程立即可见。在调用 get() 时,读取 state 能获取最新的任务进度;在提交任务后,主线程通过检查 state 判断任务是否完成,而工作线程更新 state 后,其他等待的线程能立即感知到这一变化。

FutureTask 中 volatile 如何保证状态可见性
FutureTask 中的 volatile 主要用于修饰 int state 字段。这个字段记录了任务的五种状态:NATIVE、RUNNING、RAN、CANCELLED、EXCEPTIONAL 和 COMPLETING。当工作线程执行完任务后,它会更新 state 为 RAN 或 EXCEPTIONAL。由于该字段是 volatile 的,这个写操作具有内存屏障效应,确保了对 state 的修改对所有其他线程立即可见。这样,任何调用 get() 的线程都能读到最新的状态值,从而决定是继续阻塞还是返回结果。

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class VolatileDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
Callable<String> task = () -> {
Thread.sleep(1000);
return "Result";
};
FutureTask<String> futureTask = new FutureTask<>(task);
new Thread(futureTask).start();
System.out.println("Waiting...");
String result = futureTask.get(); // 阻塞直到 state 变为 RAN/EXCEPTIONAL
System.out.println(result);
}
}预期输出:
Waiting...
Result
立即学习“Java免费学习笔记(深入)”;
volatile 与 Unsafe.compareAndSwapInt 的配合机制
虽然 state 是 volatile 的,但 FutureTask 还需要原子性地从一种状态转换到另一种状态(例如从 NATIVE 到 RUNNING)。为此,它使用 sun.misc.Unsafe.compareAndSwapInt(this, STATE_OFFSET, expectedValue, newValue)。这里 volatile 保证了读取到的 expectedValue 是当前最新的,而 CAS 操作保证了状态更新的原子性。如果 CAS 失败,说明有其他线程已经修改了状态,当前线程需要重试。这种组合确保了在多核环境下,任务状态的流转是安全且一致的。

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class CASDemo {
public static void main(String[] args) {
Callable<String> task = () -> "Done";
FutureTask<String> ft = new FutureTask<>(task);
// 模拟多线程竞争启动
Thread t1 = new Thread(ft);
Thread t2 = new Thread(ft);
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch (InterruptedException e) {}
System.out.println(ft.isDone()); // true
}
}预期输出:
true
当 state 为 RAN 时 get() 的行为是什么
当 state 被更新为 RAN 时,意味着任务已成功执行完毕并设置了结果。此时,任何调用 get() 的线程都会发现 state 不再是 NATIVE 或 RUNNING,而是 RAN。代码会直接返回之前存储在 outcome 字段中的结果值,而不会进入阻塞队列等待。这是因为 volatile 保证了 state 的可见性,使得线程能立即感知到任务已完成,从而高效地获取结果。


















