CompletableFuture 源码深度解析:WaitNode、CompletionNode 与 Completion 回调机制

CompletableFuture 是 Java 8 引入的功能强大的异步编程工具,既可以像 Future 一样阻塞等待结果,也支持非阻塞的回调链式编程。
在它的源码内部,等待机制与回调机制的核心支撑,正是以下三个关键结构:

  • WaitNode:管理阻塞等待的线程
  • CompletionNode:管理回调任务
  • Completion:回调任务的抽象基类

本文将直接从 JDK 源码出发,逐行分析这三者的设计与实现原理。


1. 关键成员变量

CompletableFuture 内部,和等待、回调相关的核心字段有三个:

volatile Object result;           // 任务结果或异常包装对象(AltResult)
volatile WaitNode waiters;        // Treiber 栈,保存等待线程
volatile CompletionNode completions; // Treiber 栈,保存回调任务

1.1 result

  • 保存任务完成后的状态。
  • 如果是正常完成,直接保存结果值。
  • 如果是异常完成,保存的是 AltResult(内部类)用于封装异常。

1.2 waiters

  • 保存所有调用 get()join() 等需要阻塞等待的线程。
  • 使用 Treiber 栈(无锁单链表)存储,每个节点是一个 WaitNode

1.3 completions

  • 保存所有在任务完成后需要触发的回调任务。
  • 也是 Treiber 栈结构,每个节点是 CompletionNode,内部封装了一个 Completion 对象。

2. WaitNode —— 阻塞线程的管理

源码:

static final class WaitNode implements ForkJoinPool.ManagedBlocker {
    long nanos;          // 等待时间(纳秒),仅定时等待时使用
    final long deadline; // 截止时间(纳秒时间戳),仅定时等待时使用
    volatile int interruptControl; // >0 可中断, <0 已中断
    volatile Thread thread;        // 等待的线程
    volatile WaitNode next;        // Treiber 栈指针

    WaitNode(boolean interruptible, long nanos, long deadline) {
        this.thread = Thread.currentThread();
        this.interruptControl = interruptible ? 1 : 0;
        this.nanos = nanos;
        this.deadline = deadline;
    }

    public boolean isReleasable() {
        if (thread == null)
            return true; // 已唤醒
        if (Thread.interrupted()) {
            int i = interruptControl;
            interruptControl = -1;
            if (i > 0)
                return true; // 响应中断
        }
        if (deadline != 0L &&
            (nanos <= 0L || (nanos = deadline - System.nanoTime()) <= 0L)) {
            thread = null; // 超时
            return true;
        }
        return false;
    }

    public boolean block() {
        if (isReleasable())
            return true;
        else if (deadline == 0L)
            LockSupport.park(this); // 无限等待
        else if (nanos > 0L)
            LockSupport.parkNanos(this, nanos); // 定时等待
        return isReleasable();
    }
}

2.1 设计特点

  • 实现了 ForkJoinPool.ManagedBlocker
    这意味着当 CompletableFuture 运行在 ForkJoinPool 中时,阻塞不会过度占用线程池的并行度。
    ForkJoinPool 会在阻塞时可能补充额外的线程。

  • 中断控制
    interruptControl 通过 >0 / <0 来标识当前节点是否支持中断,以及是否已被中断。

  • Treiber 栈结构
    WaitNode.next 构成无锁链表,通过 CAS 入栈和出栈,避免锁竞争。


3. CompletionNode —— 回调任务的节点

源码:

static final class CompletionNode {
    final Completion completion; // 回调任务对象
    volatile CompletionNode next; // Treiber 栈指针

    CompletionNode(Completion completion) {
        this.completion = completion;
    }
}

3.1 作用

CompletionNodecompletions 栈的节点,它不像 WaitNode 保存线程,而是保存一个 回调任务,任务的类型是 Completion 抽象类的子类。

CompletableFuture 完成时,postComplete() 方法会遍历 completions 栈中的所有节点,依次触发它们的 tryFire() 方法来执行回调。


4. Completion —— 回调任务的抽象基类

源码(简化):

@SuppressWarnings("serial")
abstract static class Completion extends AtomicInteger implements Runnable {
    abstract CompletableFuture<?> tryFire(int mode);
}

4.1 设计要点

  • 继承 AtomicInteger
    利用 compareAndSet 来保证一个回调任务只会被执行一次(原子状态控制)。

  • 实现 Runnable
    方便直接在异步线程池中执行。

  • 核心方法 tryFire(int mode)

    • mode 通常表示执行模式(SYNC/ASYNC)。
    • 子类会实现该方法,在依赖任务完成后触发目标任务。
    • 例如:
      • UniApply:处理 thenApply 回调
      • BiApply:处理 thenCombine 回调
      • OrApply:处理 applyToEither 回调

5. 三者协作机制

  1. 阻塞等待路径(WaitNode)

    • 调用 get() / join() 时,如果任务未完成,构造一个 WaitNode 入栈。
    • 任务完成时,postComplete() 会遍历 waiters 栈,使用 LockSupport.unpark(thread) 唤醒等待线程。
  2. 非阻塞回调路径(CompletionNode + Completion)

    • 注册回调(thenApply、thenAccept 等)时,会构造对应的 Completion 子类,并封装到 CompletionNode 入栈。
    • 任务完成时,postComplete() 会遍历 completions 栈,调用每个 CompletiontryFire() 方法执行回调。
  3. Treiber 栈的使用

    • waiterscompletions 都是 Treiber 栈,入栈时使用 CAS,无需锁。
    • 遍历时是 LIFO 顺序,回调执行和唤醒线程是倒序的。

6. 总结

CompletableFuture 的高性能异步回调与阻塞等待机制,并不是依赖复杂的锁,而是通过 Treiber 栈 + CAS + 轻量节点结构 实现的。

  • WaitNode:管理阻塞等待的线程,配合 ManagedBlocker 友好地支持 ForkJoinPool。
  • CompletionNode:管理回调任务的链表节点。
  • Completion:抽象回调逻辑的基类,提供统一的执行接口。

这种分离式的设计,使得 CompletableFuture 能够同时高效支持阻塞和非阻塞两种模式,满足各种并发场景的需求。

posted @ 2025-08-12 21:00  零1零1  阅读(45)  评论(0)    收藏  举报