FutureTask通过状态机控制、内存可见性机制和原子协作模式确保准确性:相同输入只执行一次计算,所有并发请求获得一致结果或异常。

FutureTask 本身不直接“保障准确性”,而是通过状态机控制 + 内存可见性机制 + 原子协作模式,确保多个线程对同一任务的调用最终获得一致、正确且仅执行一次的结果。准确性在这里指:相同输入只触发一次真实计算,所有并发请求拿到的是同一个正确结果(或同一个异常),不会因竞态导致重复执行、结果不一致或状态混乱。
靠状态机防止重复执行
FutureTask 内部用 volatile int state 精确标记生命周期(NEW → COMPLETING → NORMAL/EXCEPTIONAL 等)。任务只能从 NEW 状态被 CAS 更新为 COMPLETING,再变为终态;一旦进入终态,state 不再改变,outcome(结果或异常)也被唯一写入。这意味着:
- 多个线程尝试调用 run() 时,只有第一个成功将 state 从 NEW 改为 COMPLETING 的线程真正执行 call();
- 其余线程调用 run() 会直接返回(因为 state 已非 NEW),不会重复计算;
- get() 方法严格依赖 state 判断是否可读 outcome,避免读到未初始化或中间状态的值。
靠 happens-before 保证结果可见性
FutureTask 在设置结果时,先完成 outcome = v 赋值,再用 UNSAFE.putOrderedInt 将 state 设为 NORMAL。由于 state 是 volatile 字段,该写操作构成一个释放动作(release action);而 get() 中读取 state 构成获取动作(acquire action)。JMM 规定:释放动作与后续获取动作之间存在 happens-before 关系——因此,只要 get() 成功返回,就一定能看到之前写入的 outcome 值,不会出现“结果已算完但读到 null 或旧值”的情况。
靠原子协作避免缓存污染
单独使用 FutureTask 并不能自动解决高并发下的重复计算问题;它必须配合 ConcurrentHashMap 和 putIfAbsent 才能发挥最大价值:
立即学习“Java免费学习笔记(深入)”;
- ConcurrentHashMap.putIfAbsent(key, ft) 是原子操作,确保同 key 只插入一个 FutureTask 实例;
- 插入成功的线程负责调用 ft.run() 启动计算;
- 其他线程拿到已存在的 ft,调用 get() 阻塞等待——它们共享的是同一个 FutureTask 实例,自然共享同一个 outcome;
- 若计算抛出异常,ExecutionException 包装后统一暴露给所有调用者,行为一致。
靠健壮处理守住边界
生产环境中,准确性还需覆盖异常路径:
- 遇到 CancellationException:说明任务被取消,应立即 cache.remove(key, ft),防止后续请求拿到失效 Future;
- 遇到 ExecutionException:表明计算逻辑失败,同样需清理缓存,避免下次请求复用错误状态;
- 必须使用带超时的 get(timeout, unit):防止某次计算卡死,导致所有等待线程永久挂起,间接破坏服务可用性与结果时效性。


















