【并发】---Disruptor 原理、源码与实战 深度解析

基于 :LMAX Disruptor 4.0.0(2023-09-29 发布,Java 11+);文内标注 ⚠️ 4.0 变更 处为与 3.4.4 的差异

本文回答三个问题:① 为什么 Disruptor 比 ArrayBlockingQueue 快一个数量级?② 这份性能是怎么用「环形数组 + 序号 + 内存屏障」实现的(逐行源码)?③ 落到业务里该怎么写(可直接运行的完整代码)?


目录

  1. 核心理论与定位
    • [1.1 一句话定义](#1.1 一句话定义)
    • [1.2 传统阻塞队列的三大性能杀手](#1.2 传统阻塞队列的三大性能杀手)
    • [1.3 性能对比数据](#1.3 性能对比数据)
    • [1.4 Disruptor 的解法总表](#1.4 Disruptor 的解法总表)
  2. [核心概念与数据结构 ★★★](#核心概念与数据结构 ★★★)
    • [2.1 RingBuffer:环形数组](#2.1 RingBuffer:环形数组)
    • [2.2 Sequence:序号](#2.2 Sequence:序号)
    • [2.3 Sequencer:序号发生器](#2.3 Sequencer:序号发生器)
    • [2.4 SequenceBarrier:序号屏障](#2.4 SequenceBarrier:序号屏障)
    • [2.5 WaitStrategy:等待策略](#2.5 WaitStrategy:等待策略)
    • [2.6 EventProcessor / EventHandler](#2.6 EventProcessor / EventHandler)
    • [2.7 概念对照表](#2.7 概念对照表)
  3. [为什么快:七大核心机制 ★★★](#为什么快:七大核心机制 ★★★)
    • [3.1 机制一:预分配环形数组(零 GC)](#3.1 机制一:预分配环形数组(零 GC))
    • [3.2 机制二:序号驱动,用 CAS 替代锁](#3.2 机制二:序号驱动,用 CAS 替代锁)
    • [3.3 机制三:缓存行填充,消除伪共享](#3.3 机制三:缓存行填充,消除伪共享)
    • [3.4 机制四:批量消费,摊薄同步开销](#3.4 机制四:批量消费,摊薄同步开销)
    • [3.5 机制五:单生产者免 CAS 快路径](#3.5 机制五:单生产者免 CAS 快路径)
    • [3.6 机制六:序号屏障构成并行依赖图](#3.6 机制六:序号屏障构成并行依赖图)
    • [3.7 机制七:全链路无垃圾](#3.7 机制七:全链路无垃圾)
  4. 整体流程与入口
  5. [源码逐步剖析 ★★★](#源码逐步剖析 ★★★)
    • [5.1 步骤一:Disruptor 构造](#5.1 步骤一:Disruptor 构造)
    • [5.2 步骤二:RingBuffer 与字段布局](#5.2 步骤二:RingBuffer 与字段布局)
    • [5.3 步骤三:handleEventsWith 建立依赖图](#5.3 步骤三:handleEventsWith 建立依赖图)
    • [5.4 步骤四:start 启动消费线程](#5.4 步骤四:start 启动消费线程)
    • [5.5 步骤五:生产者 next() 申请槽位](#5.5 步骤五:生产者 next() 申请槽位)
    • [5.6 步骤六:publish() 发布与内存屏障](#5.6 步骤六:publish() 发布与内存屏障)
    • [5.7 步骤七:消费者 waitFor 与批量分发](#5.7 步骤七:消费者 waitFor 与批量分发)
    • [5.8 步骤八:Sequence 推进的三种写语义](#5.8 步骤八:Sequence 推进的三种写语义)
  6. 关键类关系图
  7. 等待策略全解与选型
  8. [实战代码 ★★★](#实战代码 ★★★)
    • [8.1 依赖引入](#8.1 依赖引入)
    • [8.2 Hello World:最小可运行](#8.2 Hello World:最小可运行)
    • [8.3 订单异步处理:菱形依赖图](#8.3 订单异步处理:菱形依赖图)
    • [8.4 EventTranslator 三种写法](#8.4 EventTranslator 三种写法)
    • [8.5 Spring Boot 集成](#8.5 Spring Boot 集成)
    • [8.6 EventPoller:非阻塞拉取(Netty/推送场景)](#8.6 EventPoller:非阻塞拉取(Netty/推送场景))
    • [8.7 压测代码](#8.7 压测代码)
    • [8.8 4.0 新特性:批次回退 Rewind](#8.8 4.0 新特性:批次回退 Rewind)
  9. [常见坑与 FAQ](#常见坑与 FAQ)
    • [9.1 十个必踩的坑](#9.1 十个必踩的坑)
    • [9.2 FAQ](#9.2 FAQ)
  10. 参考资料

1. 核心理论与定位

1.1 一句话定义

一句话概括 :Disruptor 是 LMAX 交易所开源的单机进程内、无锁、有界的高性能线程间消息传递框架------它把"队列"从一个需要在头尾加锁的数据结构,改造成了"一个预先分配好的环形数组 + 一组不断递增的序号"。

它解决的不是"分布式消息"问题,而是单 JVM 内两个线程之间如何以纳秒级延迟、千万级 TPS 传递数据的问题。典型定位:

维度 说明
定位 进程内队列(In-Process Queue),不是 MQ
模型 有界(RingBuffer 定长)、无锁(CAS + 内存屏障)、无垃圾(可零分配)
支持模式 单/多生产者 × 多消费者;支持广播(同一事件多个消费者各消费一份)、并行、串行(流水线)、菱形依赖
延迟量级 优雅策略下 P99 约 百纳秒级;BusySpin 下平均延迟可低至几十纳秒
吞吐量级 单机 千万级 ops/s (对比 ArrayBlockingQueue 百万级)
已知使用者 LMAX 交易所核心撮合、Log4j2 AsyncLogger、Apache Storm、Dubbo(部分场景)、Netty 生态、HBase
依赖 零第三方依赖,单 jar,源码量小(核心约 60 个类)

#mermaid-svg-8x5YQFanXK50CVO3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8x5YQFanXK50CVO3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8x5YQFanXK50CVO3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8x5YQFanXK50CVO3 .error-icon{fill:#552222;}#mermaid-svg-8x5YQFanXK50CVO3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8x5YQFanXK50CVO3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8x5YQFanXK50CVO3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8x5YQFanXK50CVO3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8x5YQFanXK50CVO3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8x5YQFanXK50CVO3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8x5YQFanXK50CVO3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8x5YQFanXK50CVO3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8x5YQFanXK50CVO3 .marker.cross{stroke:#333333;}#mermaid-svg-8x5YQFanXK50CVO3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8x5YQFanXK50CVO3 p{margin:0;}#mermaid-svg-8x5YQFanXK50CVO3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8x5YQFanXK50CVO3 .cluster-label text{fill:#333;}#mermaid-svg-8x5YQFanXK50CVO3 .cluster-label span{color:#333;}#mermaid-svg-8x5YQFanXK50CVO3 .cluster-label span p{background-color:transparent;}#mermaid-svg-8x5YQFanXK50CVO3 .label text,#mermaid-svg-8x5YQFanXK50CVO3 span{fill:#333;color:#333;}#mermaid-svg-8x5YQFanXK50CVO3 .node rect,#mermaid-svg-8x5YQFanXK50CVO3 .node circle,#mermaid-svg-8x5YQFanXK50CVO3 .node ellipse,#mermaid-svg-8x5YQFanXK50CVO3 .node polygon,#mermaid-svg-8x5YQFanXK50CVO3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8x5YQFanXK50CVO3 .rough-node .label text,#mermaid-svg-8x5YQFanXK50CVO3 .node .label text,#mermaid-svg-8x5YQFanXK50CVO3 .image-shape .label,#mermaid-svg-8x5YQFanXK50CVO3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-8x5YQFanXK50CVO3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8x5YQFanXK50CVO3 .rough-node .label,#mermaid-svg-8x5YQFanXK50CVO3 .node .label,#mermaid-svg-8x5YQFanXK50CVO3 .image-shape .label,#mermaid-svg-8x5YQFanXK50CVO3 .icon-shape .label{text-align:center;}#mermaid-svg-8x5YQFanXK50CVO3 .node.clickable{cursor:pointer;}#mermaid-svg-8x5YQFanXK50CVO3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8x5YQFanXK50CVO3 .arrowheadPath{fill:#333333;}#mermaid-svg-8x5YQFanXK50CVO3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8x5YQFanXK50CVO3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8x5YQFanXK50CVO3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8x5YQFanXK50CVO3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8x5YQFanXK50CVO3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8x5YQFanXK50CVO3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8x5YQFanXK50CVO3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8x5YQFanXK50CVO3 .cluster text{fill:#333;}#mermaid-svg-8x5YQFanXK50CVO3 .cluster span{color:#333;}#mermaid-svg-8x5YQFanXK50CVO3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8x5YQFanXK50CVO3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8x5YQFanXK50CVO3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-8x5YQFanXK50CVO3 .icon-shape,#mermaid-svg-8x5YQFanXK50CVO3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8x5YQFanXK50CVO3 .icon-shape p,#mermaid-svg-8x5YQFanXK50CVO3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8x5YQFanXK50CVO3 .icon-shape .label rect,#mermaid-svg-8x5YQFanXK50CVO3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8x5YQFanXK50CVO3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8x5YQFanXK50CVO3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8x5YQFanXK50CVO3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 单个 JVM 进程
next() 抢槽位
publish() 发布
waitFor() 等待
生产者线程

Producer
RingBuffer

环形数组
消费者 1

BatchEventProcessor
消费者 2

BatchEventProcessor
消费者 3

BatchEventProcessor
全程无锁

仅 CAS + 内存屏障

关键认知Disruptor 不是一个"更好的队列",而是一种"共享内存式"的并发编程模型。 你要接受它的两个核心约束------① 事件对象是复用的 ,消费者不能长期持有引用;② 缓冲区是有界的,满了生产者必须自旋等待。接受了这两点,才能换来数量级的性能提升。


1.2 传统阻塞队列的三大性能杀手

ArrayBlockingQueue(下文简称 ABQ)为参照物,它是怎么慢下来的?

杀手一:锁(Lock)的竞争与上下文切换

ArrayBlockingQueue.put() / take() 内部用 ReentrantLock + 两个 Condition

java 复制代码
// java.util.concurrent.ArrayBlockingQueue 精简
public void put(E e) throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();          // ← 竞争:可能挂起、可能让出 CPU
    try {
        while (count == items.length)
            notFull.await();           // ← 队列满:线程进入等待队列,发生上下文切换
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

问题在于:

问题 后果
每次操作都要 lock/unlock,即使无竞争也要执行 CAS + 内存屏障 基线开销高
有竞争时,未抢到锁的线程被 LockSupport.park() 挂起 上下文切换成本 ~ 1~10 μs
唤醒后还要重新抢锁(AQS 队列) 延迟抖动大,P99 差
锁保护的是"整个数组 + head/tail/count" 生产者和消费者互相阻塞,即使操作的是不同槽位

关键认知 :锁的本质问题是它把"两个互不相干的线程"强行变成了"串行"。生产者写 index=5、消费者读 index=3,本来毫无冲突,但锁让它们必须排队。

杀手二:伪共享(False Sharing)

CPU 缓存以 缓存行(Cache Line,通常 64 字节) 为单位在 L1/L2/L3 之间搬运。如果两个变量位于同一缓存行,且被不同核心频繁写,就会发生"写失效风暴":

复制代码
        ┌──────────── 同一个 64 字节缓存行 ────────────┐
        │  head = 3   |   tail = 7   |   count = 4     │
        └──────────────────────────────────────────────┘
              ▲                    ▲
              │ 写                 │ 写
        生产者核心0          消费者核心1
              │                    │
              └──── 每次都让对方缓存失效,反复从 L3 重拉 ────┘

ABQ 的 headtailcount 三个字段紧挨着声明------生产者和消费者各写各的,却共享同一个缓存行,性能凭空损失数倍。

杀手三:GC(对象分配与回收)

ABQ 每 put 一个元素就产生一个 Node/对象引用,高吞吐下:

复制代码
每秒 1000 万个事件 → 每秒 1000 万个对象 → Young GC 频繁触发 → STW 停顿 → 延迟毛刺

在低延迟场景(如交易所撮合)中,一次 50ms 的 GC 停顿等于灾难


1.3 性能对比数据

以下为 LMAX 官方技术论文(Disruptor: High performance alternative to bounded queues for exchanging data between concurrent threads)给出的基准数据,测试场景为 1 亿条消息通过队列传递:

Unicast(1 生产者 → 1 消费者)

实现 吞吐量 (ops/sec) 平均延迟 P99 延迟 P99.99 延迟
ArrayBlockingQueue 5,339,256 299 ms 350 ms 574 ms
LinkedBlockingQueue 3,448,795 546 ms 704 ms 1,008 ms
Disruptor(BlockingWaitStrategy) 4,936,373 348 ms 380 ms 560 ms
Disruptor(BusySpinWaitStrategy) 25,998,336 79 ms 79 ms 87 ms

Pipelined(1 生产者 → 3 消费者流水线)

实现 吞吐量 (ops/sec) 平均延迟 P99 延迟
ArrayBlockingQueue 5,539,506 695 ms 1,056 ms
Disruptor(BusySpin) 15,540,798 78 ms 81 ms

Sequencer(3 生产者 → 1 消费者)

实现 吞吐量 (ops/sec)
ArrayBlockingQueue 2,397,307
LinkedBlockingQueue 1,125,040
Disruptor(BusySpin) 10,856,341

⚠️ 注意口径:上表是官方论文在特定硬件(2011 年前后的服务器)与特定测试参数下的结果,绝对值不能直接外推到你的机器。可传递的结论只有两条:

  1. BusySpinWaitStrategy 下,Disruptor 的吞吐是 ABQ 的 4~6 倍,延迟只有其 1/4
  2. BlockingWaitStrategy 下二者吞吐接近 ------因为此时瓶颈又回到"阻塞/唤醒"上了,说明等待策略的选择比框架本身更能决定性能

1.4 Disruptor 的解法总表

传统队列的问题 Disruptor 的对策 对应源码
加锁串行化 序号(Sequence)+ CAS,生产者只 CAS 一个 cursor,消费者只写自己的 sequence MultiProducerSequencer#next
头尾指针互相阻塞 消费者用独立的 Sequence并推进,与生产者解耦 Sequence
生产者不知道消费者进度 Gating Sequence:生产前计算所有消费者"最小序号" Util#getMinimumSequence
边界判断难(环形数组) availableBuffer + generation flag,用"轮次"判定槽位是否可覆盖 MultiProducerSequencer#isAvailable
伪共享 缓存行填充LhsPadding / RhsPadding / RingBufferPad Sequence 类继承链
频繁 GC 事件对象预分配 + 原地填充(EventFactory) RingBufferFields#entries
逐个传递的低效同步 批量消费 :一次 waitFor 拿一批,连续处理 BatchEventProcessor#run
内存可见性靠锁 显式内存屏障 :4.0 用 VarHandle.releaseFence/acquireFence/fullFence(3.x 用 Unsafe.putOrderedLong / putLongVolatile Sequence#set/setVolatile
单生产者还要 CAS ProducerType.SINGLE 走免 CAS 快路径 SingleProducerSequencer

2. 核心概念与数据结构 ★★★

2.1 RingBuffer:环形数组

RingBuffer 是 Disruptor 的数据容器,本质是一个会"绕圈"的定长数组:

复制代码
容量 8 的 RingBuffer,当前已发布到序号 11:

 index:    0      1      2      3      4      5      6      7
        ┌──────┬──────┬──────┬──────┬──────┬──────┬──────┬──────┐
 entries│ ev 8 │ ev 9 │ev 10 │ev 11 │ ev 4 │ ev 5 │ ev 6 │ ev 7 │
        └──────┴──────┴──────┴──────┴──────┴──────┴──────┴──────┘
          ▲             ▲
          │             │
     序号 8 的槽位    序号 11 的槽位(cursor 指向这里)
     对应 8 & 0x7=0

 序号 12 将落回 index 4(12 & 0x7 = 4),覆盖旧的序号 4

两个关键设计点:

  1. 容量必须是 2 的幂 。这样 sequence & (bufferSize - 1) 就等价于取模,比 % 快得多(源码:indexMask = bufferSize - 1)。

    java 复制代码
    public static int ceilingNextPowerOfTwo(final int x)
    {
        return 1 << (32 - Integer.numberOfLeadingZeros(x - 1));
    }
  2. 槽位里放的是"事件对象",而不是数据本身 。事件对象在构造时一次性创建 ,之后只做 原地 set 字段。这是"零 GC"的根基。

关键认知 :RingBuffer 是一个覆盖写(overwrite)的环形队列。它不扩容,只会覆盖旧数据。因此必须保证"生产者的写指针不能追上最慢的消费者" ------ 这个约束由 gating sequence 机制强制保证,也正是"有界"的全部含义。

2.2 Sequence:序号

Sequence 是整个框架的原子基石,它做了两件事:

  1. 用一个 volatile long 保存序号;
  2. 用 7 个 long 字段在自己前后各填充 56 字节,把它单独隔离在一个(或两个)缓存行里,彻底消除伪共享。
java 复制代码
// com.lmax.disruptor.Sequence ------ 4.0.0 的实际实现(继承链 + 三种写语义)
class LhsPadding
{
    // 前填充 56 字节。4.0 用 56 个 byte 字段拼出来(3.x 是 7 个 long p1..p7,字节数相同)
    protected byte p10, p11, p12, p13, p14, p15, p16, p17;   // 8 字节
    protected byte p20, p21, p22, p23, p24, p25, p26, p27;   // 8 字节
    protected byte p30, p31, p32, p33, p34, p35, p36, p37;
    protected byte p40, p41, p42, p43, p44, p45, p46, p47;
    protected byte p50, p51, p52, p53, p54, p55, p56, p57;
    protected byte p60, p61, p62, p63, p64, p65, p66, p67;
    protected byte p70, p71, p72, p73, p74, p75, p76, p77;   // 合计 56 字节
}

class Value extends LhsPadding
{
    // ⚠️ 4.0 里它不再是 volatile ------ 可见性改由 VarHandle + 显式栅栏精确控制
    protected long value;
}

class RhsPadding extends Value
{
    // 后填充同样是 56 个 byte(p90..p97 / p100..p107 / ... / p150..p157)
}

public class Sequence extends RhsPadding
{
    static final long INITIAL_VALUE = -1L;              // 初始 -1,所以第一个可用序号是 0
    private static final VarHandle VALUE_FIELD;         // 4.0 改用 VarHandle,摆脱 sun.misc.Unsafe

    static
    {
        try
        {
            VALUE_FIELD = MethodHandles.lookup()
                    .findVarHandle(Sequence.class, "value", long.class);
        }
        catch (final Exception e)
        {
            throw new RuntimeException(e);
        }
    }

    public Sequence()
    {
        this(INITIAL_VALUE);
    }

    public Sequence(final long initialValue)
    {
        // 3.x 是 UNSAFE.putOrderedLong(...);4.0 等价写法:release 栅栏 + 普通写
        VarHandle.releaseFence();
        this.value = initialValue;
    }

    public long get()
    {
        final long v = this.value;
        VarHandle.acquireFence();          // 等价于 3.x 的 volatile 读
        return v;
    }

    public void set(final long value)
    {
        // lazySet 语义:release 栅栏 + 普通写(3.x:putOrderedLong)
        // 生产者/消费者"推进进度"用这个,最便宜
        VarHandle.releaseFence();
        this.value = value;
    }

    public void setVolatile(final long value)
    {
        // volatile 写语义:release 栅栏 + 普通写 + full 栅栏(3.x:putLongVolatile)
        // 需要让其它线程"立刻看到"时用它
        VarHandle.releaseFence();
        this.value = value;
        VarHandle.fullFence();
    }

    public boolean compareAndSet(final long expectedValue, final long newValue)
    {
        return VALUE_FIELD.compareAndSet(this, expectedValue, newValue);
    }

    public long incrementAndGet()
    {
        return (long) VALUE_FIELD.getAndAdd(this, 1L) + 1L;
    }

    public long addAndGet(final long increment)
    {
        return (long) VALUE_FIELD.getAndAdd(this, increment) + increment;
    }
}

💡 4.0 的一个容易被忽略的底层重写 :3.x 整个框架重度依赖 sun.misc.Unsafe;4.0 把 Sequence 改成了 VarHandle + VarHandle.releaseFence()/acquireFence()/fullFence() 显式栅栏RingBufferFields 也从 UNSAFE.getObject(entries, REF_ARRAY_BASE + ...) 换回了普通数组下标访问 。这是因为最低版本提到 Java 11 后,VarHandle 已经能覆盖原来 Unsafe 的全部能力,而显式栅栏比 lazySet/volatile 更可控。读 4.0 源码时不要再去找 UNSAFE
关键认知Sequence 的三种写语义是整个框架性能的关键:

  • set() (3.x:UNSAFE.putOrderedLong;4.0:releaseFence + 普通写)------ store-store 语义,不刷新 store buffer,快。用在"推进进度"这类不需要立即可见的地方。
  • setVolatile() (3.x:putLongVolatile;4.0:releaseFence + 写 + fullFence)------ 全屏障,立即对其他核心可见,慢。用在"发布数据、必须让别人看到"的地方。
  • compareAndSet() ------ 用于多生产者抢序号。

什么时候用哪个,是读 Disruptor 源码时最值得反复琢磨的地方。

2.3 Sequencer:序号发生器

Sequencer并发控制的核心,负责回答两个问题:

  • 生产者问 :"我能写到哪个序号?(槽位空着吗?)" → next(n)
  • 消费者问 :"我能读到哪个序号?(数据写好了吗?)" → 通过 SequenceBarrier

它有两个实现,对应两种并发模型:

实现类 适用场景 next() 的并发手段 性能
SingleProducerSequencer 只有一个线程调用 next() 无 CAS,只用普通写 + 边界检查 最快(无原子操作)
MultiProducerSequencer 多线程并发调用 next() cursor.compareAndSet(current, next) 自旋 略慢(有 CAS 竞争)

⚠️ 这是最容易用错的地方 :如果你声明了 ProducerType.SINGLE(默认值),但实际有多个线程调用 ringBuffer.publishEvent(...),会静默地数据错乱 ------不报错,但事件会互相覆盖。反过来,如果你只有一个生产者却用了 MULTI,只是白付了 CAS 的成本,不会出错。

拿不准就用 MULTI

两个实现内部还各自用填充类(SingleProducerSequencerPad / SingleProducerSequencerFields)把 nextValuecachedValue 等热字段隔开。

2.4 SequenceBarrier:序号屏障

消费者不能直接读 RingBuffer------它必须先问屏障:"我要的序号 k 准备好了吗?"

java 复制代码
public interface SequenceBarrier
{
    /** 等待 sequence 变为可消费,返回实际可消费到的最大序号 */
    long waitFor(long sequence) throws AlertException, InterruptedException, TimeoutException;

    /** 获取当前游标(依赖的上游最小序号) */
    long getCursor();

    /** 是否被中断(用于停机) */
    boolean isAlerted();
    void alert();
    void clearAlert();
    void checkAlert() throws AlertException;
}

屏障做了三件事:

  1. 等待 :委托给 WaitStrategy,直到上游序号 ≥ 目标序号;
  2. 依赖 :它依赖的不是"生产者游标"这一个值,而是所有前置消费者的最小序号 (通过 FixedSequenceGroup 或单个 Sequence);
  3. 可见性 :处理多生产者下的"洞"------调用 sequencer.getHighestPublishedSequence() 找到真正连续可用的上界。

关键认知SequenceBarrier 是 Disruptor 实现**依赖图(Dependency Graph)**的载体。你在 DSL 里写 .then().after(),最终都变成"某个消费者的 SequenceBarrier 依赖另一组 Sequence"。这就是它能做"流水线 / 菱形 / 并行分支"的原因。

2.5 WaitStrategy:等待策略

策略 实现方式 CPU 占用 延迟 适用场景
BusySpinWaitStrategy 死循环自旋 极高(占满一核) 最低 线程数 < CPU 核数,追求极致低延迟
YieldingWaitStrategy 自旋 + Thread.yield() 很低 线程数 < CPU 核数,允许让出 CPU
SleepingWaitStrategy 自旋 → yieldparkNanos(100ns) 默认推荐,异步日志等对延迟不极端敏感的场景
BlockingWaitStrategy ReentrantLock + Condition 高(μs 级抖动) CPU 资源紧张、吞吐优先
LiteBlockingWaitStrategy 无锁 CAS + park/unpark 3.4+ 新增,想省 CPU 又受不了重锁
TimeoutBlockingWaitStrategy 带超时的阻塞(ReentrantLock 可用于停机等待;4.0 已改为 synchronizedGC-free
LiteTimeoutBlockingWaitStrategy 带超时的无锁阻塞 4.0 新增,LiteBlocking 的超时版
PhasedBackoffWaitStrategy 分阶段组合(自旋 → yield → 阻塞) 吞吐/延迟折中,可自定义各阶段

源码(SleepingWaitStrategy,注意其三段式降级):

java 复制代码
public final class SleepingWaitStrategy implements WaitStrategy
{
    private static final int DEFAULT_RETRIES = 200;
    private static final long DEFAULT_SLEEP = 100;   // 纳秒

    private final int retries;
    private final long sleepTimeNs;

    @Override
    public long waitFor(final long sequence, Sequence cursor, final Sequence dependentSequence,
                        final SequenceBarrier barrier) throws AlertException, InterruptedException
    {
        long availableSequence;
        int counter = retries;

        while ((availableSequence = dependentSequence.get()) < sequence)
        {
            counter = applyWaitMethod(barrier, counter);
            if (Thread.currentThread().isInterrupted())
            {
                throw new InterruptedException();
            }
        }

        return availableSequence;
    }

    private int applyWaitMethod(final SequenceBarrier barrier, int counter) throws AlertException
    {
        barrier.checkAlert();                 // 每轮都检查是否需要停机

        if (counter > 100)
        {
            --counter;                        // 阶段一:纯自旋
        }
        else if (counter > 0)
        {
            --counter;
            Thread.onSpinWait();              // 阶段二:提示 CPU 进入低功耗自旋(JDK9+)
        }
        else
        {
            LockSupport.parkNanos(sleepTimeNs); // 阶段三:真正挂起 100 纳秒
        }

        return counter;
    }
}

⚠️ 4.0 变更ThreadHints.onSpinWait() 已废弃,改用 JDK 9 的 Thread.onSpinWait()

2.6 EventProcessor / EventHandler

组件 角色
EventHandler<T> 业务接口 ,你实现 onEvent(T event, long sequence, boolean endOfBatch)
EventProcessor 消费线程本体,内部循环 waitFor → 取事件 → 回调 EventHandler
BatchEventProcessor 唯一的(4.0 后)标准实现,批量消费
ExceptionHandler 消费者抛异常时的兜底策略
LifecycleAware 生命周期回调(onStart / onShutdown)⚠️ 4.0 已合并进 EventHandler 的默认方法
TimeoutHandler waitFor 超时时的回调(配合 TimeoutBlockingWaitStrategy
java 复制代码
public interface EventHandler<T>
{
    void onEvent(T event, long sequence, boolean endOfBatch) throws Exception;
}

endOfBatch批量消费的边界标记 :当 sequence == availableSequence 时为 true。它给了你一个天然的"刷盘/批量提交"时机------例如异步日志在 endOfBatch 时才 flush(),能显著降低 I/O 次数。

⚠️ 4.0 变更BatchStartAwareLifecycleAwareSequenceReportingEventHandler 三个接口被合并为 EventHandler 的 default 方法。4.0 的实际继承结构是:

java 复制代码
// 包级私有接口,用户代码不实现它,只实现 EventHandler
interface EventHandlerBase<T> extends EventHandlerIdentity
{
    void onEvent(T event, long sequence, boolean endOfBatch) throws Throwable;
    default void onBatchStart(long batchSize, long queueDepth) { }   // ← 原 BatchStartAware
    default void onStart() { }                                        // ← 原 LifecycleAware
    default void onShutdown() { }                                     // ← 原 LifecycleAware
    default void onTimeout(long sequence) throws Exception { }
}

public interface EventHandler<T> extends EventHandlerBase<T>
{
    void onEvent(T event, long sequence, boolean endOfBatch) throws Exception;
    default void setSequenceCallback(Sequence sequence) { }           // ← 原 SequenceReportingEventHandler
}

所以 3.x 里 implements EventHandler<T>, LifecycleAware 的写法,在 4.0 里要改成只 implements EventHandler<T> 并直接覆写 onStart() / onShutdown() 。另外 onBatchStart 的参数从"只有 batchSize(实际是 queueDepth)"改成了明确的 (batchSize, queueDepth) 两个参数。

⚠️ 同时 4.0 移除了 WorkerPool / WorkProcessorhandleEventsWithWorkerPool(见 FAQ Q7)------需要"任务竞争式消费"(一条消息只被一个消费者处理)的场景,要自己在用户态实现。

2.7 概念对照表

Disruptor 概念 类比 一句话职责
RingBuffer 数组 / 队列容器 存事件对象,靠 indexMask 取模定位槽位
Event / EventFactory 队列里的元素 预创建、可复用的数据载体
Sequence 指针 一个被填充保护、可原子推进的 long
Sequencer 队列的"生产者端" 分配序号、管理 gating、维护 availableBuffer
SequenceBarrier 队列的"消费者端" 等待可消费序号、实现依赖关系
WaitStrategy Condition.await 决定"等不到数据时怎么办"
EventProcessor 消费者线程 循环拉取并回调业务 Handler
EventHandler 业务监听器 写业务逻辑的地方
ProducerType --- 声明单/多生产者,决定是否走 CAS
Disruptor 门面 / Builder 装配 DSL、启动、停机

3. 为什么快:七大核心机制 ★★★

3.1 机制一:预分配环形数组(零 GC)

事件对象在 RingBuffer 创建时全部创建好,此后只做原地写入:

java 复制代码
// RingBufferFields 构造:一次性填满数组(完整字段布局见 3.3 与 5.2)
private void fill(final EventFactory<E> eventFactory)
{
    for (int i = 0; i < bufferSize; i++)
    {
        // 注意下标要加 BUFFER_PAD 偏移,元素放在数组的"有效区"
        entries[BUFFER_PAD + i] = eventFactory.newInstance();
    }
}

发布一个事件的完整过程没有任何对象分配

java 复制代码
// translator.translateTo(event, sequence) 只是往已存在的对象里 set 字段
ringBuffer.publishEvent((event, sequence) -> {
    event.setOrderId(1001L);        // 原地写字段,不 new
    event.setAmount(99.5);
});

收益:稳态下 Young GC 次数趋近于 0,延迟毛刺消失。


3.2 机制二:序号驱动,用 CAS 替代锁

生产者不需要锁住整个数组,它只需要原子地抢占一个序号:

java 复制代码
// MultiProducerSequencer.next ------ 核心就是一个 CAS 自旋
do
{
    current = cursor.get();
    next = current + n;

    long wrapPoint = next - bufferSize;               // 覆盖线:不能越过这里
    long cachedGatingSequence = gatingSequenceCache.get();

    if (wrapPoint > cachedGatingSequence || cachedGatingSequence > current)
    {
        long gatingSequence = Util.getMinimumSequence(gatingSequences, current);
        if (wrapPoint > gatingSequence)
        {
            LockSupport.parkNanos(1);                 // 缓冲区满,自旋等待
            continue;
        }
        gatingSequenceCache.set(gatingSequence);
    }
    else if (cursor.compareAndSet(current, next))
    {
        break;                                        // 抢到了 [current+1, next] 这段序号
    }
}
while (true);

为什么这比锁快?

维度 ReentrantLock CAS
无竞争路径 CAS + 可能 park 一次 CAS
有竞争时 未抢到者被挂起(上下文切换 μs 级) 自旋重试(ns 级)
是否阻塞消费者 是(同一把锁) ,消费者只推进自己的 Sequence
内存语义 由 AQS 保证 volatile 语义保证,粗粒度但足够

关键认知 :CAS 的优势不在于"更快",而在于竞争时不做上下文切换。当临界区极短(只是改一个 long)时,自旋重试远比挂起再唤醒划算。


3.3 机制三:缓存行填充,消除伪共享

Disruptor 对三类热点数据结构都做了填充:

(1)Sequence ------ 三明治填充

java 复制代码
class LhsPadding  { protected byte p10..p77; }              // 56 字节前填充(3.x:7 个 long)
class Value extends LhsPadding { protected long value; }    // 价值字段被夹在中间
class RhsPadding extends Value { protected byte p90..p157; }// 56 字节后填充
public class Sequence extends RhsPadding { ... }

(2)RingBuffer ------ RingBufferPad + 数组两端填充

java 复制代码
abstract class RingBufferPad
{
    protected byte p10, p11, ..., p77;      // 同样是 56 字节
}

abstract class RingBufferFields<E> extends RingBufferPad
{
    private static final int BUFFER_PAD = 32;     // 4.0 直接写死 32;3.x 算作 128 / scale
    private final long indexMask;
    private final E[] entries;                    // 真正的存储
    protected final int bufferSize;
    protected final Sequencer sequencer;

    RingBufferFields(final EventFactory<E> eventFactory, final Sequencer sequencer)
    {
        super(sequencer);
        this.bufferSize = sequencer.getBufferSize();

        if (bufferSize < 1) { throw new IllegalArgumentException("bufferSize must not be less than 1"); }
        if (Integer.bitCount(bufferSize) != 1) { throw new IllegalArgumentException("bufferSize must be a power of 2"); }

        this.indexMask = bufferSize - 1;
        this.entries = new Object[bufferSize + 2 * BUFFER_PAD];   // ⚠️ 前后各留 32 个元素 = 128 字节
        fill(eventFactory);
    }

    private void fill(final EventFactory<E> eventFactory)
    {
        for (int i = 0; i < bufferSize; i++)
        {
            entries[BUFFER_PAD + i] = eventFactory.newInstance();  // 预创建全部事件对象
        }
    }

    // 4.0:普通数组下标访问(3.x 是 UNSAFE.getObject(entries, REF_ARRAY_BASE + ...))
    protected final E elementAt(long sequence)
    {
        return entries[BUFFER_PAD + (int) (sequence & indexMask)];
    }
}

注意 new Object[bufferSize + 2 * BUFFER_PAD]------数组两端各塞 128 字节的 padding (在指针压缩开启时 BUFFER_PAD = 32 个引用 × 4 字节 = 128 字节 = 2 个缓存行)。目的是防止"数组头对象(含 length 等元数据)"与"第 0 号元素"、"最后一个元素"与"相邻对象"共享缓存行;同时让"绕圈时的边界"少一些 corner case。注意访问时要统一加 BUFFER_PAD 偏移,这是读这段源码时最容易看漏的地方。

(3)SequencernextValue / cachedValue

SingleProducerSequencerPad / SingleProducerSequencerFields 两层继承,把 nextValuecachedValue 与父类字段隔开;SingleProducerSequencer 自身又用 p10..p77 做了第三层填充------为了两个热字段,Disruptor 在这里套了三层类

⚠️ 注意 :如果你的 JDK 开启了 -XX:-RestrictContended,也可以用 @sun.misc.Contended 注解达到同样效果,但 Disruptor 选择手动填充以保证跨 JDK 兼容。


3.4 机制四:批量消费,摊薄同步开销

BatchEventProcessor 一次 waitFor 可以拿到一大批可消费的序号:

java 复制代码
final long availableSequence = sequenceBarrier.waitFor(nextSequence);

while (nextSequence <= availableSequence)          // ← 一次性处理一整批
{
    event = dataProvider.get(nextSequence);
    eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence);
    nextSequence++;
}

sequence.set(availableSequence);                   // ← 进度只写一次!

关键收益sequence.set() 是带内存屏障的写操作。如果每处理一条就写一次,1000 条就要 1000 次屏障;批量处理后只写一次。在高吞吐下这一项就能带来 2~3 倍提升。


3.5 机制五:单生产者免 CAS 快路径

SingleProducerSequencernext() 完全没有原子操作 ------因为只有一个写入者,nextValue 是"自己私有"的:

java 复制代码
public long next(int n)
{
    long nextValue = this.nextValue;
    long nextSequence = nextValue + n;
    long wrapPoint = nextSequence - bufferSize;
    long cachedGatingSequence = this.cachedValue;

    if (wrapPoint > cachedGatingSequence || cachedGatingSequence > nextValue)
    {
        cursor.setVolatile(nextValue);              // StoreLoad 屏障:发布进度给别人看
        long minSequence;
        while (wrapPoint > (minSequence = Util.getMinimumSequence(gatingSequences, nextValue)))
        {
            LockSupport.parkNanos(1L);
        }
        this.cachedValue = minSequence;
    }

    this.nextValue = nextSequence;                  // 纯本地写,无原子操作
    return nextSequence;
}

再看 publish() 里一个极其精妙的次序

java 复制代码
public void publish(long sequence)
{
    cursor.set(sequence);          // ① lazySet:数值已写入 entries 后,再发布进度(store-store 有序)
    waitStrategy.signalAllWhenBlocking();   // ② 唤醒阻塞中的消费者
}

① 必须写在"业务把数据填进 entries 之后"(这是由 RingBuffer.translateAndPublishtry/finally 保证的);② 的顺序也重要------先把 cursor 推上去,再发信号。


3.6 机制六:序号屏障构成并行依赖图

假设你要实现:订单事件 → 并行做「风控」和「审计」→ 两者都完成后 → 「入库」
#mermaid-svg-1S9RoZtImvFxfBSo{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1S9RoZtImvFxfBSo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1S9RoZtImvFxfBSo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1S9RoZtImvFxfBSo .error-icon{fill:#552222;}#mermaid-svg-1S9RoZtImvFxfBSo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1S9RoZtImvFxfBSo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1S9RoZtImvFxfBSo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1S9RoZtImvFxfBSo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1S9RoZtImvFxfBSo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1S9RoZtImvFxfBSo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1S9RoZtImvFxfBSo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1S9RoZtImvFxfBSo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1S9RoZtImvFxfBSo .marker.cross{stroke:#333333;}#mermaid-svg-1S9RoZtImvFxfBSo svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1S9RoZtImvFxfBSo p{margin:0;}#mermaid-svg-1S9RoZtImvFxfBSo .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1S9RoZtImvFxfBSo .cluster-label text{fill:#333;}#mermaid-svg-1S9RoZtImvFxfBSo .cluster-label span{color:#333;}#mermaid-svg-1S9RoZtImvFxfBSo .cluster-label span p{background-color:transparent;}#mermaid-svg-1S9RoZtImvFxfBSo .label text,#mermaid-svg-1S9RoZtImvFxfBSo span{fill:#333;color:#333;}#mermaid-svg-1S9RoZtImvFxfBSo .node rect,#mermaid-svg-1S9RoZtImvFxfBSo .node circle,#mermaid-svg-1S9RoZtImvFxfBSo .node ellipse,#mermaid-svg-1S9RoZtImvFxfBSo .node polygon,#mermaid-svg-1S9RoZtImvFxfBSo .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1S9RoZtImvFxfBSo .rough-node .label text,#mermaid-svg-1S9RoZtImvFxfBSo .node .label text,#mermaid-svg-1S9RoZtImvFxfBSo .image-shape .label,#mermaid-svg-1S9RoZtImvFxfBSo .icon-shape .label{text-anchor:middle;}#mermaid-svg-1S9RoZtImvFxfBSo .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1S9RoZtImvFxfBSo .rough-node .label,#mermaid-svg-1S9RoZtImvFxfBSo .node .label,#mermaid-svg-1S9RoZtImvFxfBSo .image-shape .label,#mermaid-svg-1S9RoZtImvFxfBSo .icon-shape .label{text-align:center;}#mermaid-svg-1S9RoZtImvFxfBSo .node.clickable{cursor:pointer;}#mermaid-svg-1S9RoZtImvFxfBSo .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1S9RoZtImvFxfBSo .arrowheadPath{fill:#333333;}#mermaid-svg-1S9RoZtImvFxfBSo .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1S9RoZtImvFxfBSo .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1S9RoZtImvFxfBSo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1S9RoZtImvFxfBSo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1S9RoZtImvFxfBSo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1S9RoZtImvFxfBSo .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1S9RoZtImvFxfBSo .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1S9RoZtImvFxfBSo .cluster text{fill:#333;}#mermaid-svg-1S9RoZtImvFxfBSo .cluster span{color:#333;}#mermaid-svg-1S9RoZtImvFxfBSo div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1S9RoZtImvFxfBSo .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1S9RoZtImvFxfBSo rect.text{fill:none;stroke-width:0;}#mermaid-svg-1S9RoZtImvFxfBSo .icon-shape,#mermaid-svg-1S9RoZtImvFxfBSo .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1S9RoZtImvFxfBSo .icon-shape p,#mermaid-svg-1S9RoZtImvFxfBSo .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1S9RoZtImvFxfBSo .icon-shape .label rect,#mermaid-svg-1S9RoZtImvFxfBSo .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1S9RoZtImvFxfBSo .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1S9RoZtImvFxfBSo .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1S9RoZtImvFxfBSo :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生产者
RingBuffer
风控 Handler
审计 Handler
入库 Handler

对应 DSL:

java 复制代码
disruptor.handleEventsWith(riskHandler, auditHandler)   // 并行分支,各拿到一份事件
         .then(storeHandler);                           // 汇聚:等两者都完成

源码层面:storeHandlerSequenceBarrierdependentSequenceriskHandlerauditHandler 两个 Sequence 组成的 FixedSequenceGroup

java 复制代码
// AbstractSequencer
public SequenceBarrier newBarrier(Sequence... sequencesToTrack)
{
    return new ProcessingSequenceBarrier(this, waitStrategy, cursor, sequencesToTrack);
}

// FixedSequenceGroup ------ 取组内最小序号,"都完成"即"最小值达标"
public final class FixedSequenceGroup extends Sequence
{
    private final Sequence[] sequences;

    @Override
    public long get()
    {
        return Util.getMinimumSequence(sequences);
    }
}

关键认知Disruptor 的"依赖编排"不是调度框架,而是"消费者各自阻塞在不同的序号条件上"。 没有任何中心调度器,所有协作都通过"读别人的 Sequence"完成。这是它能做到极低开销的原因。


3.7 机制七:全链路无垃圾

官方在 3.0 起就把"GC-free"作为硬指标,体现在:

位置 手段
事件对象 预分配 + 原地复用
Sequence 数组 gatingSequences 用数组而非 List 迭代(虽然 addGatingSequences 会 recreate,但只在构建期)
waitFor 返回值 基本类型 long,不装箱
异常处理 AlertException.INSTANCE 单例(故意不填 stack trace,避免每次停机都构造栈)
TimeoutBlockingWaitStrategy 4.0 用 synchronized 块替代 ReentrantLock,避免每次 await 产生 Condition 节点
java 复制代码
// AlertException ------ 单例且无栈,专为"用它做控制流"而设计
public final class AlertException extends Exception
{
    public static final AlertException INSTANCE = new AlertException();

    private AlertException() { }

    @Override
    public Throwable fillInStackTrace() { return this; }   // ← 关键:不做栈回溯
}

注意这个模式:用异常做控制流通常被诟病"慢",但这里通过覆写 fillInStackTrace() 消掉了唯一的开销。


4. 整体流程与入口

图 4-1 Disruptor 整体流程:构建期(单线程装配)→ 运行期(多线程并发)→ 停机期

入口清单(读源码建议按这个顺序):

顺序 看什么
1 Disruptor 门面、DSL、start/shutdown
2 RingBuffer / RingBufferFields 数据布局、padding、translateAndPublish
3 AbstractSequencer gatingSequences 管理
4 MultiProducerSequencer CAS + availableBuffer
5 SingleProducerSequencer 免 CAS 快路径
6 ProcessingSequenceBarrier waitFor + getHighestPublishedSequence
7 BatchEventProcessor 消费循环
8 *WaitStrategy 等待策略
9 Sequence 内存语义

5. 源码逐步剖析 ★★★

5.1 步骤一:Disruptor 构造

java 复制代码
// com.lmax.disruptor.dsl.Disruptor ------ 精简
public class Disruptor<T>
{
    private final RingBuffer<T> ringBuffer;
    private final Executor executor;
    private final ConsumerRepository<T> consumerRepository = new ConsumerRepository<>();
    private final AtomicBoolean started = new AtomicBoolean(false);
    private ExceptionHandler<? super T> exceptionHandler = new FatalExceptionHandler();

    public Disruptor(final EventFactory<T> eventFactory, final int ringBufferSize,
                     final ThreadFactory threadFactory)
    {
        this(RingBuffer.createMultiProducer(eventFactory, ringBufferSize), threadFactory);
    }

    public Disruptor(final EventFactory<T> eventFactory, final int ringBufferSize,
                     final ThreadFactory threadFactory, final ProducerType producerType,
                     final WaitStrategy waitStrategy)
    {
        this(RingBuffer.create(eventFactory, producerType, ringBufferSize, waitStrategy), threadFactory);
    }

    private Disruptor(final RingBuffer<T> ringBuffer, final ThreadFactory threadFactory)
    {
        this.ringBuffer = ringBuffer;
        this.executor = Executors.newCachedThreadPool(threadFactory);   // 每个消费者一个线程
    }
}

⚠️ 4.0 变更 :接受 Executor 的构造器已被移除,统一改为 ThreadFactory。生产环境请务必传具名线程工厂 (见 8.5 的 NamedThreadFactory),否则线上 dump 时全是 pool-N-thread-M

一个细节Executors.newCachedThreadPool(threadFactory)------线程数无上限,每个 handler 一个线程。所以不要在一个 JVM 里建几十个 Disruptor 实例,线程会爆。


5.2 步骤二:RingBuffer 与字段布局

java 复制代码
// RingBuffer.create
public static <E> RingBuffer<E> create(final EventFactory<E> factory, final ProducerType producerType,
                                       final int bufferSize, final WaitStrategy waitStrategy)
{
    switch (producerType)
    {
        case SINGLE:
            return createSingleProducer(factory, bufferSize, waitStrategy);
        case MULTI:
            return createMultiProducer(factory, bufferSize, waitStrategy);
        default:
            throw new IllegalStateException(producerType.toString());
    }
}

RingBufferFields 里的 8 个关键字段:

java 复制代码
abstract class RingBufferFields<E> extends RingBufferPad
{
    private static final int BUFFER_PAD = 32;  // 数组两端各留 32 个引用(指针压缩下 = 128 字节)
    private final long indexMask;              // bufferSize - 1,取模用
    private final E[] entries;                 // 真正的存储,长度 = bufferSize + 2 * BUFFER_PAD
    protected final int bufferSize;
    protected final Sequencer sequencer;

    RingBufferFields(final EventFactory<E> eventFactory, final Sequencer sequencer)
    {
        super(sequencer);
        this.bufferSize = sequencer.getBufferSize();

        if (bufferSize < 1)
        {
            throw new IllegalArgumentException("bufferSize must not be less than 1");
        }
        if (Integer.bitCount(bufferSize) != 1)
        {
            throw new IllegalArgumentException("bufferSize must be a power of 2");
        }

        this.indexMask = bufferSize - 1;
        this.entries = new Object[bufferSize + 2 * BUFFER_PAD];   // 字节码:new Object[bufferSize + 64]
        fill(eventFactory);
    }

    /** 唯一的取值入口:注意 + BUFFER_PAD 偏移 */
    protected final E elementAt(final long sequence)
    {
        return entries[BUFFER_PAD + (int) (sequence & indexMask)];
    }

    E get(final long sequence)
    {
        return elementAt(sequence);
    }
}

⚠️ 两个读源码时的坑

  1. Integer.bitCount(bufferSize) != 1 ------ 容量不是 2 的幂会在构造时直接抛异常 。所以 new Disruptor<>(OrderEvent::new, 1000, ...) 是错的,要写 1024(或用 Util.ceilingNextPowerOfTwo(1000))。
  2. 取元素必须加 BUFFER_PAD 偏移 ------ 数组实际长度是 bufferSize + 64,序号 s 对应的下标是 (s & indexMask) + 32。3.x 里这个偏移藏在 REF_ARRAY_BASE 里,4.0 直接写成 + BUFFER_PAD,反而更好读。

5.3 步骤三:handleEventsWith 建立依赖图

java 复制代码
// Disruptor#handleEventsWith
@SafeVarargs
public final EventHandlerGroup<T> handleEventsWith(final EventHandler<? super T>... handlers)
{
    return createEventProcessors(new Sequence[0], handlers);
}

EventHandlerGroup<T> createEventProcessors(final Sequence[] barrierSequences,
                                           final EventHandler<? super T>[] eventHandlers)
{
    checkNotStarted();                                  // 启动后不允许再改图

    final Sequence[] processorSequences = new Sequence[eventHandlers.length];
    // ① 建屏障:依赖 barrierSequences(首个 handler 时为空)
    final SequenceBarrier barrier = ringBuffer.newBarrier(barrierSequences);

    for (int i = 0, len = eventHandlers.length; i < len; i++)
    {
        final EventHandler<? super T> eventHandler = eventHandlers[i];

        // ② 每个 handler 一个 BatchEventProcessor
        final BatchEventProcessor<T> batchEventProcessor =
                new BatchEventProcessor<>(ringBuffer, barrier, eventHandler);

        if (exceptionHandler != null)
        {
            batchEventProcessor.setExceptionHandler(exceptionHandler);
        }

        // ③ 注册到 ConsumerRepository
        consumerRepository.add(batchEventProcessor, eventHandler, barrier);
        processorSequences[i] = batchEventProcessor.getSequence();
    }

    // ④ 把本组消费者的 Sequence 注册为"生产者的 gating"------关键!
    updateGatingSequencesForNextInChain(barrierSequences, processorSequences);

    return new EventHandlerGroup<>(this, consumerRepository, processorSequences);
}

private void updateGatingSequencesForNextInChain(final Sequence[] barrierSequences,
                                                 final Sequence[] processorSequences)
{
    if (processorSequences.length > 0)
    {
        // 生产者之后会通过 Util.getMinimumSequence(gatingSequences) 来防止覆盖
        ringBuffer.addGatingSequences(processorSequences);
        // 本组已"接手",把上游从 gating 中摘掉(避免上游成为瓶颈)
        for (final Sequence barrierSequence : barrierSequences)
        {
            ringBuffer.removeGatingSequence(barrierSequence);
        }
        consumerRepository.unMarkEventProcessorsAsEndOfChain(barrierSequences);
    }
}

关键认知updateGatingSequencesForNextInChain 里"加本组、摘上游 "这一手非常重要。在菱形图 A → B → C 中,生产者只需要看 B 的进度就够了(B 又必须等 A),摘掉 A 可减少 getMinimumSequence 的遍历量,也避免 A 拖慢生产。


5.4 步骤四:start 启动消费线程

java 复制代码
// Disruptor#start
public RingBuffer<T> start()
{
    checkOnlyStartedOnce();                              // AtomicBoolean CAS,二次调用抛异常
    for (final ConsumerInfo consumerInfo : consumerRepository)
    {
        consumerInfo.start(executor);                    // executor.execute(batchEventProcessor)
    }
    return ringBuffer;
}

注意 start() 并没有"启动"生产者 ------生产者就是你自己调用 ringBuffer.publishEvent() 的线程。start() 只是把 N 个 BatchEventProcessor(都是 Runnable)提交到线程池。


5.5 步骤五:生产者 next() 申请槽位

MultiProducerSequencer.next() 已经在 3.2 展开,这里补充两个易被忽略的细节

细节 A:gatingSequenceCache 是必需品,不是优化

java 复制代码
if (wrapPoint > cachedGatingSequence || cachedGatingSequence > current)
{
    // 只有"可能追上消费者"时,才去遍历所有 gating sequence
    long gatingSequence = Util.getMinimumSequence(gatingSequences, current);
    ...
    gatingSequenceCache.set(gatingSequence);
}

如果每轮 都遍历所有消费者序号,N 个消费者就是 N 次 volatile 读------成本高且造成缓存行争抢。用 cachedGatingSequence 做本地缓存后,绝大多数循环只需一次本地读。

细节 B:缓冲区满时用 LockSupport.parkNanos(1) 而非 WaitStrategy

java 复制代码
if (wrapPoint > gatingSequence)
{
    LockSupport.parkNanos(1);   // ← 硬编码 1 纳秒,不委托给 WaitStrategy
    continue;
}

源码里带着一句 // TODO, should we spin based on the wait strategy?这是一个已知的粗糙设计 :即使消费者用了 BusySpinWaitStrategy,生产者满时也只能 parkNanos。理解这一点有助于解释"为什么我换了策略性能没变"。

实践推论 :如果你追求极致吞吐,应当保证 RingBuffer 永远不满(容量给足 + 消费者够快),这样生产者走的就是零阻塞的快路径。


5.6 步骤六:publish() 发布与内存屏障

RingBuffer 侧的发布入口:

java 复制代码
// RingBuffer
public void publish(final long sequence)
{
    sequencer.publish(sequence);
}

public <A> void publishEvent(final EventTranslatorOneArg<E, A> translator, final A arg)
{
    final long sequence = sequencer.next();
    translateAndPublish(translator, sequence, arg);
}

private <A> void translateAndPublish(final EventTranslatorOneArg<E, A> translator,
                                     final long sequence, final A arg)
{
    try
    {
        translator.translateTo(elementAt(sequence), sequence, arg);  // ① 先写数据
    }
    finally
    {
        sequencer.publish(sequence);                                 // ② 再发布序号(finally 保证)
    }
}

⚠️ 注意 finally :即使业务在填充事件时抛异常,序号也必须发布出去 ,否则消费者会永远卡在这个序号上(这是"序号空洞"问题)。这个设计选择意味着:如果你的 translateTo 抛了异常,消费者会收到一个"字段可能只填了一半"的事件 。所以 translateTo 里只应做"赋值",不要做有副作用的业务调用。

MultiProducerSequencer.publish

java 复制代码
public void publish(final long sequence)
{
    setAvailable(sequence);                  // ① 标记该槽位"当前轮次已就绪"
    waitStrategy.signalAllWhenBlocking();    // ② 唤醒阻塞的消费者
}

private void setAvailable(final long sequence)
{
    setAvailableBufferValue(calculateIndex(sequence), calculateAvailabilityFlag(sequence));
}

private static final VarHandle AVAILABLE_ARRAY = MethodHandles.arrayElementVarHandle(int[].class);

private void setAvailableBufferValue(int index, int flag)
{
    // 4.0 写法(3.x:UNSAFE.putOrderedInt,lazySet 语义)
    AVAILABLE_ARRAY.setRelease(availableBuffer, index, flag);
}

private int calculateAvailabilityFlag(final long sequence)
{
    return (int) (sequence >>> indexShift);   // 轮次 = sequence / bufferSize
}

private int calculateIndex(final long sequence)
{
    return ((int) sequence) & indexMask;      // index = sequence % bufferSize
}

这是多生产者下解决"序号空洞"的核心机制

复制代码
bufferSize = 8, indexShift = 3

生产者 A 抢到序号 12 → index = 12 & 7 = 4,flag = 12 >>> 3 = 1
生产者 B 抢到序号 13 → index = 13 & 7 = 5,flag = 1
若 B 先写完并 publish(13),则 availableBuffer[5] = 1
A 后 publish(12),availableBuffer[4] = 1

消费者问 isAvailable(12):availableBuffer[4] == 1 ? 是 → 可读
消费者问 isAvailable(13):availableBuffer[5] == 1 ? 是 → 可读

注意 flag 用"轮次"而非固定值,天然区分了"上一轮的旧数据":
序号 4 → index 4,flag = 4 >>> 3 = 0
序号 12 → index 4,flag = 1     ← 同一个槽位,flag 不同,不会误判

消费者端据此裁剪出连续可用的上界:

java 复制代码
public long getHighestPublishedSequence(long lowerBound, long availableSequence)
{
    for (long sequence = lowerBound; sequence <= availableSequence; sequence++)
    {
        if (!isAvailable(sequence))
        {
            return sequence - 1;      // 遇到第一个空洞,就停在它前面
        }
    }
    return availableSequence;
}

性能提示 :这个 for 循环在"生产者并发度高、发布顺序乱"时会多跑几圈。优化办法是让生产者尽量按序号顺序发布 (例如单生产者场景),或者用 ProducerType.SINGLE 完全绕开------单生产者模式下 getHighestPublishedSequence 直接返回 availableSequence,没有循环。

多生产者的 isAvailablesetAvailableBufferValue 是一对配对的内存序操作

java 复制代码
public boolean isAvailable(final long sequence)
{
    final int index = calculateIndex(sequence);
    final int flag = calculateAvailabilityFlag(sequence);
    // 4.0:getAcquire(3.x:UNSAFE.getIntVolatile)
    return (int) AVAILABLE_ARRAY.getAcquire(availableBuffer, index) == flag;
}

生产者侧 setRelease(release 语义)、消费者侧 getAcquire(acquire 语义)------这一对 release/acquire 保证了"生产者写入 entries 的数据"对读到 flag 的消费者必然可见。这就是"用一对精心挑选的内存序代替一把锁"的教科书例子。


5.7 步骤七:消费者 waitFor 与批量分发

java 复制代码
// ProcessingSequenceBarrier
final class ProcessingSequenceBarrier implements SequenceBarrier
{
    private final WaitStrategy waitStrategy;
    private final Sequence dependentSequence;
    private volatile boolean alerted = false;
    private final Sequence cursorSequence;
    private final Sequencer sequencer;

    ProcessingSequenceBarrier(final Sequencer sequencer, final WaitStrategy waitStrategy,
                              final Sequence cursorSequence, final Sequence[] dependentSequences)
    {
        this.sequencer = sequencer;
        this.waitStrategy = waitStrategy;
        this.cursorSequence = cursorSequence;
        if (0 == dependentSequences.length)
        {
            dependentSequence = cursorSequence;                   // 无前置:直接看生产者游标
        }
        else
        {
            dependentSequence = new FixedSequenceGroup(dependentSequences);  // 有前置:取最小
        }
    }

    @Override
    public long waitFor(final long sequence)
            throws AlertException, InterruptedException, TimeoutException
    {
        checkAlert();                                              // 停机检查

        long availableSequence = waitStrategy.waitFor(sequence, cursorSequence,
                                                      dependentSequence, this);

        if (availableSequence < sequence)
        {
            return availableSequence;
        }

        // 多生产者:裁剪到"连续可读"的上界
        return sequencer.getHighestPublishedSequence(sequence, availableSequence);
    }

    @Override
    public void alert()
    {
        alerted = true;
        waitStrategy.signalAllWhenBlocking();                      // 唤醒阻塞中的线程来响应停机
    }
}

消费主循环(BatchEventProcessor.run,4.0 加入 batchSize / rewind 后有所增补,此处为核心骨架):

java 复制代码
@Override
public void run()
{
    if (!running.compareAndSet(false, true))
    {
        throw new IllegalStateException("Thread already running");
    }

    sequenceBarrier.clearAlert();
    notifyStart();                                  // LifecycleAware#onStart

    T event = null;
    long nextSequence = sequence.get() + 1L;        // 从自己上次的进度继续

    try
    {
        while (true)
        {
            try
            {
                final long availableSequence = sequenceBarrier.waitFor(nextSequence);

                while (nextSequence <= availableSequence)
                {
                    event = dataProvider.get(nextSequence);
                    eventHandler.onEvent(event, nextSequence, nextSequence == availableSequence);
                    nextSequence++;
                }

                sequence.set(availableSequence);    // 批量推进,只写一次
            }
            catch (final TimeoutException e)
            {
                notifyTimeout(sequence.get());       // TimeoutHandler 回调
            }
            catch (final AlertException ex)
            {
                if (!running.get())
                {
                    break;                           // 正常停机路径
                }
            }
            catch (final Throwable ex)
            {
                exceptionHandler.handleEventException(ex, nextSequence, event);
                sequence.set(nextSequence);          // 跳过错行的事件,避免卡死
                nextSequence++;
            }
        }
    }
    finally
    {
        notifyShutdown();                            // LifecycleAware#onShutdown
        running.set(false);
    }
}

⚠️ 重点看异常分支 :默认的 FatalExceptionHandler重新抛出 RuntimeException,导致这个消费线程直接死掉 ------线程死了,RingBuffer 很快写满,整个链路 hang 住。生产环境必须自定义 ExceptionHandler(见 FAQ 9.2 Q3)。

停机 halt()

java 复制代码
// BatchEventProcessor#halt
public void halt()
{
    running.set(false);
    sequenceBarrier.alert();     // 让正在 waitFor 的线程抛出 AlertException 退出
}

5.8 步骤八:Sequence 推进的三种写语义

Sequence 的三种写操作与它们的实际使用位置对照,是检验是否读懂源码的最好方式:

写操作 内存语义 屏障 使用位置 为什么用它
3.xUNSAFE.putOrderedLong 4.0releaseFence + 普通写(set lazySet / release StoreStore BatchEventProcessor 推进消费进度、SingleProducerSequencer.publish 推 cursor、MultiProducerSequencer.setAvailableBufferValue 只要"后续的写不重排到它之前",不需要立即可见 → 最快
3.xUNSAFE.putLongVolatile 4.0releaseFence + 写 + fullFencesetVolatile volatile 写 StoreLoad SingleProducerSequencer.next 中发布 nextValueDisruptor 构建期 需要让其它核心立刻看到,且后续还有加载 → 用全屏障
compareAndSet(4.0:VALUE_FIELD.compareAndSet 全屏障 MultiProducerSequencer.next 抢 cursor、RingBuffer.addGatingSequences 更新 gating 需要原子读改写
4.0 新增 setRelease / getAcquire release / acquire 单向 MultiProducerSequencer.availableBuffer 的写与读 成对使用即可保证数据可见,比 volatile 便宜

一句话概括Disruptor 的性能,一半来自"少用锁",另一半来自"精确控制内存屏障的强度"。 该 lazySet 的地方绝不用 volatile,该 volatile 的地方绝不省。这是"无锁编程"从能用走向高效的必经之路。


6. 关键类关系图

#mermaid-svg-GWCtxPaoIXac1bzM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-GWCtxPaoIXac1bzM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-GWCtxPaoIXac1bzM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-GWCtxPaoIXac1bzM .error-icon{fill:#552222;}#mermaid-svg-GWCtxPaoIXac1bzM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-GWCtxPaoIXac1bzM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-GWCtxPaoIXac1bzM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-GWCtxPaoIXac1bzM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-GWCtxPaoIXac1bzM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-GWCtxPaoIXac1bzM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-GWCtxPaoIXac1bzM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-GWCtxPaoIXac1bzM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-GWCtxPaoIXac1bzM .marker.cross{stroke:#333333;}#mermaid-svg-GWCtxPaoIXac1bzM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-GWCtxPaoIXac1bzM p{margin:0;}#mermaid-svg-GWCtxPaoIXac1bzM g.classGroup text{fill:#9370DB;stroke:none;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:10px;}#mermaid-svg-GWCtxPaoIXac1bzM g.classGroup text .title{font-weight:bolder;}#mermaid-svg-GWCtxPaoIXac1bzM .cluster-label text{fill:#333;}#mermaid-svg-GWCtxPaoIXac1bzM .cluster-label span{color:#333;}#mermaid-svg-GWCtxPaoIXac1bzM .cluster-label span p{background-color:transparent;}#mermaid-svg-GWCtxPaoIXac1bzM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-GWCtxPaoIXac1bzM .cluster text{fill:#333;}#mermaid-svg-GWCtxPaoIXac1bzM .cluster span{color:#333;}#mermaid-svg-GWCtxPaoIXac1bzM .nodeLabel,#mermaid-svg-GWCtxPaoIXac1bzM .edgeLabel{color:#131300;}#mermaid-svg-GWCtxPaoIXac1bzM .edgeLabel .label rect{fill:#ECECFF;}#mermaid-svg-GWCtxPaoIXac1bzM .label text{fill:#131300;}#mermaid-svg-GWCtxPaoIXac1bzM .labelBkg{background:#ECECFF;}#mermaid-svg-GWCtxPaoIXac1bzM .edgeLabel .label span{background:#ECECFF;}#mermaid-svg-GWCtxPaoIXac1bzM .classTitle{font-weight:bolder;}#mermaid-svg-GWCtxPaoIXac1bzM .node rect,#mermaid-svg-GWCtxPaoIXac1bzM .node circle,#mermaid-svg-GWCtxPaoIXac1bzM .node ellipse,#mermaid-svg-GWCtxPaoIXac1bzM .node polygon,#mermaid-svg-GWCtxPaoIXac1bzM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-GWCtxPaoIXac1bzM .divider{stroke:#9370DB;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM g.clickable{cursor:pointer;}#mermaid-svg-GWCtxPaoIXac1bzM g.classGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-GWCtxPaoIXac1bzM g.classGroup line{stroke:#9370DB;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-GWCtxPaoIXac1bzM .classLabel .label{fill:#9370DB;font-size:10px;}#mermaid-svg-GWCtxPaoIXac1bzM .relation{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-GWCtxPaoIXac1bzM .dashed-line{stroke-dasharray:3;}#mermaid-svg-GWCtxPaoIXac1bzM .dotted-line{stroke-dasharray:1 2;}#mermaid-svg-GWCtxPaoIXac1bzM #compositionStart,#mermaid-svg-GWCtxPaoIXac1bzM .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM #compositionEnd,#mermaid-svg-GWCtxPaoIXac1bzM .composition{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM #dependencyStart,#mermaid-svg-GWCtxPaoIXac1bzM .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM #dependencyStart,#mermaid-svg-GWCtxPaoIXac1bzM .dependency{fill:#333333!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM #extensionStart,#mermaid-svg-GWCtxPaoIXac1bzM .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM #extensionEnd,#mermaid-svg-GWCtxPaoIXac1bzM .extension{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM #aggregationStart,#mermaid-svg-GWCtxPaoIXac1bzM .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM #aggregationEnd,#mermaid-svg-GWCtxPaoIXac1bzM .aggregation{fill:transparent!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM #lollipopStart,#mermaid-svg-GWCtxPaoIXac1bzM .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM #lollipopEnd,#mermaid-svg-GWCtxPaoIXac1bzM .lollipop{fill:#ECECFF!important;stroke:#333333!important;stroke-width:1;}#mermaid-svg-GWCtxPaoIXac1bzM .edgeTerminals{font-size:11px;line-height:initial;}#mermaid-svg-GWCtxPaoIXac1bzM .classTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-GWCtxPaoIXac1bzM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-GWCtxPaoIXac1bzM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-GWCtxPaoIXac1bzM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 委托
cursor / gatingSequences
FixedSequenceGroup 聚合多个
newBarrier
每 Handler 一个
持有自己的进度
构造期创建事件
Disruptor
-RingBuffer<T> ringBuffer
-Executor executor
-ConsumerRepository consumerRepository
+handleEventsWith(EventHandler...) : EventHandlerGroup
+start() : RingBuffer<T>
+shutdown()
RingBuffer
-Object\[\] entries
-long indexMask
-Sequencer sequencer
+next() : long
+publish(long)
+get(long) : E
+publishEvent(EventTranslator, A)
+newPoller() : EventPoller
<<interface>>
Sequencer
+next(n) : long
+publish(long)
+newBarrier(Sequence...) : SequenceBarrier
+getMinimumSequence() : long
+getHighestPublishedSequence(long, long) : long
AbstractSequencer
#int bufferSize
#WaitStrategy waitStrategy
#Sequence cursor
#Sequence\[\] gatingSequences
SingleProducerSequencer
-long nextValue
-long cachedValue
+next(n) : long
MultiProducerSequencer
-int\[\] availableBuffer
-Sequence gatingSequenceCache
-int indexShift
+next(n) : long
+isAvailable(long) : boolean
Sequence
-volatile long value
+get() : long
+set(long)
+setVolatile(long)
+compareAndSet(long,long) : boolean
<<interface>>
SequenceBarrier
+waitFor(long) : long
+alert()
ProcessingSequenceBarrier
-Sequence dependentSequence
-WaitStrategy waitStrategy
FixedSequenceGroup
-Sequence\[\] sequences
+get() : long
<<interface>>
WaitStrategy
+waitFor(long, Sequence, Sequence, SequenceBarrier) : long
+signalAllWhenBlocking()
<<interface>>
EventProcessor
+run()
+halt()
+getSequence() : Sequence
BatchEventProcessor
-DataProvider<T> dataProvider
-EventHandler<T> eventHandler
-Sequence sequence
+run()
<<interface>>
EventHandler
+onEvent(T, long, boolean)
<<interface>>
EventFactory
+newInstance() : T
BusySpinWaitStrategy
YieldingWaitStrategy
SleepingWaitStrategy
BlockingWaitStrategy

时序图:一次完整的生产-消费
EventHandler BatchEventProcessor WaitStrategy MultiProducerSequencer RingBuffer 生产者线程 EventHandler BatchEventProcessor WaitStrategy MultiProducerSequencer RingBuffer 生产者线程 #mermaid-svg-4PWQS8WgTV5FaEEx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-4PWQS8WgTV5FaEEx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-4PWQS8WgTV5FaEEx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-4PWQS8WgTV5FaEEx .error-icon{fill:#552222;}#mermaid-svg-4PWQS8WgTV5FaEEx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-4PWQS8WgTV5FaEEx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-4PWQS8WgTV5FaEEx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-4PWQS8WgTV5FaEEx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-4PWQS8WgTV5FaEEx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-4PWQS8WgTV5FaEEx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-4PWQS8WgTV5FaEEx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-4PWQS8WgTV5FaEEx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-4PWQS8WgTV5FaEEx .marker.cross{stroke:#333333;}#mermaid-svg-4PWQS8WgTV5FaEEx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-4PWQS8WgTV5FaEEx p{margin:0;}#mermaid-svg-4PWQS8WgTV5FaEEx .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4PWQS8WgTV5FaEEx text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-4PWQS8WgTV5FaEEx .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4PWQS8WgTV5FaEEx .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-4PWQS8WgTV5FaEEx .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-4PWQS8WgTV5FaEEx .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-4PWQS8WgTV5FaEEx #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-4PWQS8WgTV5FaEEx .sequenceNumber{fill:white;}#mermaid-svg-4PWQS8WgTV5FaEEx #sequencenumber{fill:#333;}#mermaid-svg-4PWQS8WgTV5FaEEx #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-4PWQS8WgTV5FaEEx .messageText{fill:#333;stroke:none;}#mermaid-svg-4PWQS8WgTV5FaEEx .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4PWQS8WgTV5FaEEx .labelText,#mermaid-svg-4PWQS8WgTV5FaEEx .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-4PWQS8WgTV5FaEEx .loopText,#mermaid-svg-4PWQS8WgTV5FaEEx .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-4PWQS8WgTV5FaEEx .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-4PWQS8WgTV5FaEEx .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-4PWQS8WgTV5FaEEx .noteText,#mermaid-svg-4PWQS8WgTV5FaEEx .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-4PWQS8WgTV5FaEEx .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4PWQS8WgTV5FaEEx .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4PWQS8WgTV5FaEEx .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-4PWQS8WgTV5FaEEx .actorPopupMenu{position:absolute;}#mermaid-svg-4PWQS8WgTV5FaEEx .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-4PWQS8WgTV5FaEEx .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-4PWQS8WgTV5FaEEx .actor-man circle,#mermaid-svg-4PWQS8WgTV5FaEEx line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-4PWQS8WgTV5FaEEx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} dependentSequence.get() >= nextSequence ? loop 批量处理 publishEvent(translator, order) next() CAS(cursor: cur → cur+1) sequence = cur+1 translator.translateTo(entry, seq) 写入 entries publish(seq) availableBufferidx = seq >>> shift signalAllWhenBlocking() waitFor(nextSequence) availableSequence getHighestPublishedSequence(from, to) 连续可用上界 get(seq) event(复用对象) onEvent(event, seq, endOfBatch) sequence.set(availableSequence)


7. 等待策略全解与选型

7.1 决策树

#mermaid-svg-1aTl2GFP8YOWz8O3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-1aTl2GFP8YOWz8O3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-1aTl2GFP8YOWz8O3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-1aTl2GFP8YOWz8O3 .error-icon{fill:#552222;}#mermaid-svg-1aTl2GFP8YOWz8O3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-1aTl2GFP8YOWz8O3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-1aTl2GFP8YOWz8O3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-1aTl2GFP8YOWz8O3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-1aTl2GFP8YOWz8O3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-1aTl2GFP8YOWz8O3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-1aTl2GFP8YOWz8O3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-1aTl2GFP8YOWz8O3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-1aTl2GFP8YOWz8O3 .marker.cross{stroke:#333333;}#mermaid-svg-1aTl2GFP8YOWz8O3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-1aTl2GFP8YOWz8O3 p{margin:0;}#mermaid-svg-1aTl2GFP8YOWz8O3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-1aTl2GFP8YOWz8O3 .cluster-label text{fill:#333;}#mermaid-svg-1aTl2GFP8YOWz8O3 .cluster-label span{color:#333;}#mermaid-svg-1aTl2GFP8YOWz8O3 .cluster-label span p{background-color:transparent;}#mermaid-svg-1aTl2GFP8YOWz8O3 .label text,#mermaid-svg-1aTl2GFP8YOWz8O3 span{fill:#333;color:#333;}#mermaid-svg-1aTl2GFP8YOWz8O3 .node rect,#mermaid-svg-1aTl2GFP8YOWz8O3 .node circle,#mermaid-svg-1aTl2GFP8YOWz8O3 .node ellipse,#mermaid-svg-1aTl2GFP8YOWz8O3 .node polygon,#mermaid-svg-1aTl2GFP8YOWz8O3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-1aTl2GFP8YOWz8O3 .rough-node .label text,#mermaid-svg-1aTl2GFP8YOWz8O3 .node .label text,#mermaid-svg-1aTl2GFP8YOWz8O3 .image-shape .label,#mermaid-svg-1aTl2GFP8YOWz8O3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-1aTl2GFP8YOWz8O3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-1aTl2GFP8YOWz8O3 .rough-node .label,#mermaid-svg-1aTl2GFP8YOWz8O3 .node .label,#mermaid-svg-1aTl2GFP8YOWz8O3 .image-shape .label,#mermaid-svg-1aTl2GFP8YOWz8O3 .icon-shape .label{text-align:center;}#mermaid-svg-1aTl2GFP8YOWz8O3 .node.clickable{cursor:pointer;}#mermaid-svg-1aTl2GFP8YOWz8O3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-1aTl2GFP8YOWz8O3 .arrowheadPath{fill:#333333;}#mermaid-svg-1aTl2GFP8YOWz8O3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-1aTl2GFP8YOWz8O3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-1aTl2GFP8YOWz8O3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1aTl2GFP8YOWz8O3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-1aTl2GFP8YOWz8O3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1aTl2GFP8YOWz8O3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-1aTl2GFP8YOWz8O3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-1aTl2GFP8YOWz8O3 .cluster text{fill:#333;}#mermaid-svg-1aTl2GFP8YOWz8O3 .cluster span{color:#333;}#mermaid-svg-1aTl2GFP8YOWz8O3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-1aTl2GFP8YOWz8O3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-1aTl2GFP8YOWz8O3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-1aTl2GFP8YOWz8O3 .icon-shape,#mermaid-svg-1aTl2GFP8YOWz8O3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-1aTl2GFP8YOWz8O3 .icon-shape p,#mermaid-svg-1aTl2GFP8YOWz8O3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-1aTl2GFP8YOWz8O3 .icon-shape .label rect,#mermaid-svg-1aTl2GFP8YOWz8O3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-1aTl2GFP8YOWz8O3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-1aTl2GFP8YOWz8O3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-1aTl2GFP8YOWz8O3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是


不能
紧张
充裕

不能
如何选择 WaitStrategy?
对延迟敏感吗?

P99 要求 < 1ms ?
能保证消费者线程数

< CPU 物理核数吗?
CPU 资源紧张吗?
BusySpinWaitStrategy

极致低延迟
YieldingWaitStrategy

低延迟 + 让出 CPU
能接受 μs 级抖动吗?
SleepingWaitStrategy

默认推荐 ⭐
BlockingWaitStrategy

最省 CPU
LiteBlockingWaitStrategy

3.4+ 无锁阻塞

7.2 各策略源码要点

BlockingWaitStrategyReentrantLock + Condition,先看 cursor,不够再挂起。

java 复制代码
@Override
public long waitFor(final long sequence, Sequence cursor, final Sequence dependentSequence,
                    final SequenceBarrier barrier) throws AlertException, InterruptedException
{
    long availableSequence;
    if ((availableSequence = cursor.get()) < sequence)
    {
        lock.lock();
        try
        {
            while ((availableSequence = cursor.get()) < sequence)
            {
                barrier.checkAlert();
                processorNotifyCondition.await();      // 挂起,等 signalAllWhenBlocking
            }
        }
        finally
        {
            lock.unlock();
        }
    }

    // 还要等前置消费者(依赖图场景)
    while ((availableSequence = dependentSequence.get()) < sequence)
    {
        barrier.checkAlert();
    }

    return availableSequence;
}

@Override
public void signalAllWhenBlocking()
{
    lock.lock();
    try
    {
        processorNotifyCondition.signalAll();
    }
    finally
    {
        lock.unlock();
    }
}

⚠️ 注意最后那个 while 循环:它不加锁,纯自旋。这是为了处理"生产者已经发布了,但前置消费者还没追上来"的依赖场景。

YieldWaitStrategyBusySpinWaitStrategy:区别只在中途是否让出 CPU。

java 复制代码
// YieldingWaitStrategy
@Override
public long waitFor(...) throws AlertException, InterruptedException
{
    long availableSequence;
    int counter = spinTries;             // 默认 100
    while ((availableSequence = dependentSequence.get()) < sequence)
    {
        counter = applyWaitMethod(barrier, counter);
    }
    return availableSequence;
}

private int applyWaitMethod(final SequenceBarrier barrier, int counter) throws AlertException
{
    barrier.checkAlert();
    if (0 == counter)
    {
        Thread.yield();                  // 让出 CPU 时间片
    }
    else
    {
        --counter;
        Thread.onSpinWait();
    }
    return counter;
}

LiteBlockingWaitStrategy (3.4 新增):用 AtomicBoolean + LockSupport.park/unpark 替代重锁,标记为 "experimental" ,除非你能压测验证,否则不如直接用 BlockingWaitStrategy

7.3 选型速查表

场景 推荐策略 理由
异步日志、业务异步解耦 SleepingWaitStrategy CPU/延迟平衡最好,是大多数场景的默认选择
高频交易、撮合、行情分发 BusySpinWaitStrategy 延迟最低,需独占核
消费者数接近 CPU 核数 YieldingWaitStrategy 不独占核也能保持低延迟
资源受限、后台批处理 BlockingWaitStrategy CPU 占用最低
上述都拿不准 先压测 唯一真理

关键认知没有任何一种策略在所有场景下都最优。 切换策略的成本是"改一行代码",收益可能是 3~5 倍。先跑基准测试,再定策略。


8. 实战代码 ★★★

8.1 依赖引入

xml 复制代码
<!-- pom.xml -->
<properties>
    <disruptor.version>4.0.0</disruptor.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
</properties>

<dependency>
    <groupId>com.lmax</groupId>
    <artifactId>disruptor</artifactId>
    <version>${disruptor.version}</version>
</dependency>

<!-- 单元测试用(可选) -->
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter</artifactId>
    <version>5.10.2</version>
    <scope>test</scope>
</dependency>
groovy 复制代码
// build.gradle
implementation 'com.lmax:disruptor:4.0.0'

⚠️ JDK 版本 :4.0.0 要求 Java 11+ 。若你的项目还停在 Java 8,只能用 3.4.4 (两者 API 在本文涉及的核心部分一致,只是 4.0 移除了 WorkerPool 等)。注意 com.lmax 包名未变,v3 和 v4 不能同时存在于 classpath。

💡 Log4j2 用户注意:Log4j2 官方因 Java 8 兼容性无法升级到 Disruptor 4.0,仍在使用 3.4.x。详见 \[前后端流式对话技术知识点]。


8.2 Hello World:最小可运行

java 复制代码
package com.example.disruptor.demo;

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.YieldingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.lmax.disruptor.util.DaemonThreadFactory;

/**
 * 最小可运行示例:单生产者 → 单消费者
 */
public class HelloDisruptor
{
    /** ① 事件:必须是可变 POJO,有无参构造,字段用普通 setter 原地写 */
    public static class LongEvent
    {
        private long value;

        public void set(long value)
        {
            this.value = value;
        }

        public long get()
        {
            return value;
        }
    }

    public static void main(String[] args) throws InterruptedException
    {
        // ② 环形数组大小,必须是 2 的幂
        int bufferSize = 1024;

        // ③ 构造 Disruptor(4.0 起统一使用 ThreadFactory)
        //    参数依次为:事件工厂 / 容量 / 线程工厂 / 生产者类型 / 等待策略
        Disruptor<LongEvent> disruptor = new Disruptor<>(
                LongEvent::new,                       // EventFactory:预创建事件对象
                bufferSize,
                DaemonThreadFactory.INSTANCE,
                ProducerType.SINGLE,                  // 单线程发布 → 免 CAS 快路径
                new YieldingWaitStrategy());          // 低延迟等待策略

        // ④ 可选:异常处理
        //    ⚠️ 3.x 的 com.lmax.disruptor.dsl.LoggingExceptionHandler 在 4.0 已被删除,
        //    4.0 内置只剩 FatalExceptionHandler(默认,抛异常杀死消费线程)
        //    和 IgnoreExceptionHandler(静默忽略),生产环境请自定义(见 8.5)
        disruptor.setDefaultExceptionHandler(new com.lmax.disruptor.ExceptionHandler<LongEvent>()
        {
            @Override
            public void handleEventException(Throwable ex, long sequence, LongEvent event)
            {
                // 只记日志、不抛出,保证消费线程存活
                ex.printStackTrace();
            }

            @Override
            public void handleOnStartException(Throwable ex) { ex.printStackTrace(); }

            @Override
            public void handleOnShutdownException(Throwable ex) { ex.printStackTrace(); }
        });

        // ⑤ 注册消费者
        disruptor.handleEventsWith((event, sequence, endOfBatch) ->
                System.out.println("消费到 [" + event.get()
                        + "] 序号=" + sequence + " 批次末尾=" + endOfBatch));

        // ⑥ 启动消费线程
        disruptor.start();

        // ⑦ 生产者:从 RingBuffer 直接发布
        RingBuffer<LongEvent> ringBuffer = disruptor.getRingBuffer();
        for (long i = 0; i < 10; i++)
        {
            // publishEvent 内部做了 next() → translateTo() → publish(),异常安全
            ringBuffer.publishEvent((event, sequence) -> event.set(sequence));
        }

        // ⑧ 优雅停机:等待积压消费完再停(⚠️ 4.0 没有 shutdown(Duration) 重载)
        disruptor.shutdown(5, java.util.concurrent.TimeUnit.SECONDS);
        System.out.println("shutdown 完成");
    }
}

⚠️ 4.0 变更com.lmax.disruptor.dsl.LoggingExceptionHandler 在 4.0 已被删除 ,内置实现只剩 FatalExceptionHandler(默认)和 IgnoreExceptionHandler,两者的日志输出已改为 JDK 9 的 System.Logger生产环境请自行实现 ExceptionHandler(用 SLF4J 接你的日志系统),见 8.5。


8.3 订单异步处理:菱形依赖图

业务需求 :订单事件进来后 → ① 风控校验与 ② 审计留痕并行 → 两者都完成后 → ③ 落库 + ④ 推送通知。
#mermaid-svg-cz1PZUW1l4tF8PKd{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cz1PZUW1l4tF8PKd .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cz1PZUW1l4tF8PKd .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cz1PZUW1l4tF8PKd .error-icon{fill:#552222;}#mermaid-svg-cz1PZUW1l4tF8PKd .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cz1PZUW1l4tF8PKd .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cz1PZUW1l4tF8PKd .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cz1PZUW1l4tF8PKd .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cz1PZUW1l4tF8PKd .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cz1PZUW1l4tF8PKd .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cz1PZUW1l4tF8PKd .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cz1PZUW1l4tF8PKd .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cz1PZUW1l4tF8PKd .marker.cross{stroke:#333333;}#mermaid-svg-cz1PZUW1l4tF8PKd svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cz1PZUW1l4tF8PKd p{margin:0;}#mermaid-svg-cz1PZUW1l4tF8PKd .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-cz1PZUW1l4tF8PKd .cluster-label text{fill:#333;}#mermaid-svg-cz1PZUW1l4tF8PKd .cluster-label span{color:#333;}#mermaid-svg-cz1PZUW1l4tF8PKd .cluster-label span p{background-color:transparent;}#mermaid-svg-cz1PZUW1l4tF8PKd .label text,#mermaid-svg-cz1PZUW1l4tF8PKd span{fill:#333;color:#333;}#mermaid-svg-cz1PZUW1l4tF8PKd .node rect,#mermaid-svg-cz1PZUW1l4tF8PKd .node circle,#mermaid-svg-cz1PZUW1l4tF8PKd .node ellipse,#mermaid-svg-cz1PZUW1l4tF8PKd .node polygon,#mermaid-svg-cz1PZUW1l4tF8PKd .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cz1PZUW1l4tF8PKd .rough-node .label text,#mermaid-svg-cz1PZUW1l4tF8PKd .node .label text,#mermaid-svg-cz1PZUW1l4tF8PKd .image-shape .label,#mermaid-svg-cz1PZUW1l4tF8PKd .icon-shape .label{text-anchor:middle;}#mermaid-svg-cz1PZUW1l4tF8PKd .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cz1PZUW1l4tF8PKd .rough-node .label,#mermaid-svg-cz1PZUW1l4tF8PKd .node .label,#mermaid-svg-cz1PZUW1l4tF8PKd .image-shape .label,#mermaid-svg-cz1PZUW1l4tF8PKd .icon-shape .label{text-align:center;}#mermaid-svg-cz1PZUW1l4tF8PKd .node.clickable{cursor:pointer;}#mermaid-svg-cz1PZUW1l4tF8PKd .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cz1PZUW1l4tF8PKd .arrowheadPath{fill:#333333;}#mermaid-svg-cz1PZUW1l4tF8PKd .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cz1PZUW1l4tF8PKd .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cz1PZUW1l4tF8PKd .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cz1PZUW1l4tF8PKd .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cz1PZUW1l4tF8PKd .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cz1PZUW1l4tF8PKd .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cz1PZUW1l4tF8PKd .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cz1PZUW1l4tF8PKd .cluster text{fill:#333;}#mermaid-svg-cz1PZUW1l4tF8PKd .cluster span{color:#333;}#mermaid-svg-cz1PZUW1l4tF8PKd div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cz1PZUW1l4tF8PKd .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cz1PZUW1l4tF8PKd rect.text{fill:none;stroke-width:0;}#mermaid-svg-cz1PZUW1l4tF8PKd .icon-shape,#mermaid-svg-cz1PZUW1l4tF8PKd .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cz1PZUW1l4tF8PKd .icon-shape p,#mermaid-svg-cz1PZUW1l4tF8PKd .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cz1PZUW1l4tF8PKd .icon-shape .label rect,#mermaid-svg-cz1PZUW1l4tF8PKd .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cz1PZUW1l4tF8PKd .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cz1PZUW1l4tF8PKd .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cz1PZUW1l4tF8PKd :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生产者

OrderEventPublisher
RingBuffer

16384
风控 Handler

RiskEventHandler
审计 Handler

AuditEventHandler
落库 Handler

StoreEventHandler
通知 Handler

NotifyEventHandler

① 事件定义

java 复制代码
package com.example.disruptor.order;

/**
 * 订单事件:必须可复用 ------ 所有字段都是基本类型/String 引用,不做任何"只读快照"语义
 */
public class OrderEvent
{
    private long orderId;
    private long userId;
    private long amountCent;      // 金额用"分"存,避免浮点
    private String productCode;
    private int status;           // 0=新建 1=风控通过 2=风控拒绝 3=已落库 4=已通知

    public void reset()
    {
        this.orderId = 0L;
        this.userId = 0L;
        this.amountCent = 0L;
        this.productCode = null;
        this.status = 0;
    }

    // getters / setters 省略,实际项目请补全
    public long getOrderId() { return orderId; }
    public void setOrderId(long orderId) { this.orderId = orderId; }
    public long getUserId() { return userId; }
    public void setUserId(long userId) { this.userId = userId; }
    public long getAmountCent() { return amountCent; }
    public void setAmountCent(long amountCent) { this.amountCent = amountCent; }
    public String getProductCode() { return productCode; }
    public void setProductCode(String productCode) { this.productCode = productCode; }
    public int getStatus() { return status; }
    public void setStatus(int status) { this.status = status; }

    @Override
    public String toString()
    {
        return "OrderEvent{orderId=" + orderId + ", userId=" + userId
                + ", amountCent=" + amountCent + ", productCode='" + productCode
                + "', status=" + status + '}';
    }
}

② 四个 Handler

java 复制代码
package com.example.disruptor.order;

import com.lmax.disruptor.EventHandler;
import java.util.concurrent.atomic.AtomicLong;

/** 风控:只读金额/用户,决定是否拒绝 */
public class RiskEventHandler implements EventHandler<OrderEvent>
{
    private final AtomicLong passed = new AtomicLong();
    private final AtomicLong rejected = new AtomicLong();

    @Override
    public void onEvent(OrderEvent event, long sequence, boolean endOfBatch)
    {
        if (event.getAmountCent() > 5_000_000L)          // 超过 5 万,拒绝
        {
            event.setStatus(2);
            rejected.incrementAndGet();
        }
        else
        {
            event.setStatus(1);
            passed.incrementAndGet();
        }
        // endOfBatch=true 时可以做一次批量埋点上报
        if (endOfBatch)
        {
            System.out.printf("[风控] 批次结束,累计通过=%d 拒绝=%d%n",
                    passed.get(), rejected.get());
        }
    }
}

/** 审计:只读,写审计日志;不修改事件状态(并行分支中要避免写同一字段) */
public class AuditEventHandler implements EventHandler<OrderEvent>
{
    @Override
    public void onEvent(OrderEvent event, long sequence, boolean endOfBatch)
    {
        // 真实场景这里落审计表 / 发 Kafka
        if (endOfBatch)
        {
            System.out.printf("[审计] 批次处理至序号 %d%n", sequence);
        }
    }
}

/** 落库:等风控 + 审计都完成后才执行 */
public class StoreEventHandler implements EventHandler<OrderEvent>
{
    @Override
    public void onEvent(OrderEvent event, long sequence, boolean endOfBatch)
    {
        if (event.getStatus() == 2)
        {
            // 风控拒绝的不落库
            return;
        }
        // 真实场景:jdbcTemplate.update(...)  /  MyBatis 批量插入
        event.setStatus(3);
        if (endOfBatch)
        {
            System.out.printf("[落库] 批次落库至序号 %d%n", sequence);
        }
    }
}

/** 通知:落库后推送 */
public class NotifyEventHandler implements EventHandler<OrderEvent>
{
    @Override
    public void onEvent(OrderEvent event, long sequence, boolean endOfBatch)
    {
        if (event.getStatus() == 2)
        {
            return;
        }
        event.setStatus(4);
        // 真实场景:WebSocket / 站内信 / 短信
        if (endOfBatch)
        {
            System.out.printf("[通知] 批次推送到序号 %d%n", sequence);
        }
    }
}

③ 组装 + 运行

java 复制代码
package com.example.disruptor.order;

import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.lmax.disruptor.util.DaemonThreadFactory;

import java.util.concurrent.TimeUnit;

public class OrderDisruptorBootstrap
{
    public static void main(String[] args) throws Exception
    {
        int bufferSize = 16_384;              // 2 的 14 次方

        Disruptor<OrderEvent> disruptor = new Disruptor<>(
                OrderEvent::new,              // 构造期会 new 出 16384 个 OrderEvent
                bufferSize,
                DaemonThreadFactory.INSTANCE,
                ProducerType.MULTI,           // 可能有多个线程发布(HTTP 线程池)
                new BlockingWaitStrategy());  // 业务场景不需要极致延迟,省 CPU

        // ===== 依赖编排:这是 Disruptor 最强大的地方 =====
        // handleEventsWith(A, B) → A、B 并行(各自拿到同一份事件)
        // .then(C)               → 等 A、B 都处理完再执行 C
        // .then(D)               → C 完成后再执行 D
        disruptor
                .handleEventsWith(new RiskEventHandler(), new AuditEventHandler())
                .then(new StoreEventHandler())
                .then(new NotifyEventHandler());

        disruptor.start();

        RingBuffer<OrderEvent> ringBuffer = disruptor.getRingBuffer();

        // 模拟 10 个线程并发发布订单,共 10 万条
        Thread[] producers = new Thread[10];
        for (int t = 0; t < producers.length; t++)
        {
            final int tid = t;
            producers[t] = new Thread(() ->
            {
                for (int i = 0; i < 10_000; i++)
                {
                    long orderId = tid * 10_000L + i;
                    // EventTranslatorOneArg:把 orderId 传进去,避免 lambda 捕获额外对象
                    ringBuffer.publishEvent((event, sequence, id) ->
                    {
                        event.reset();                          // ⚠️ 必须重置,因为对象是复用的
                        event.setOrderId(id);
                        event.setUserId(id % 1000);
                        event.setAmountCent(1000L + (id % 100_000));
                        event.setProductCode("SKU-" + (id % 50));
                    }, orderId);
                }
            }, "producer-" + tid);
            producers[t].start();
        }

        for (Thread p : producers)
        {
            p.join();
        }

        // 优雅停机:等待积压消费完(最多 10 秒)
        disruptor.shutdown(10, TimeUnit.SECONDS);
        System.out.println("全部处理完成");
    }
}

⚠️ 两个必须注意的点

  1. event.reset() 必须调用。事件对象是复用的,上一个事件残留的字段会污染当前事件。这是最容易出的生产事故。
  2. 并行分支中的 Handler 不要写同一个字段RiskEventHandlerstatusAuditEventHandler 就不能再写 status------它们是并发的,没有顺序保证。

8.4 EventTranslator 三种写法

publishEvent 有多种重载,按参数个数的不同避免了 lambda 捕获时的装箱开销:

java 复制代码
RingBuffer<OrderEvent> rb = disruptor.getRingBuffer();

// ① 无参:只用序号
rb.publishEvent((event, sequence) -> event.setOrderId(sequence));

// ② 单参:最常用,避免为传参多创建一个"捕获对象"
rb.publishEvent((event, sequence, orderId) -> {
    event.setOrderId(orderId);
}, 1001L);

// ③ 双参
rb.publishEvent((event, sequence, userId, amount) -> {
    event.setUserId(userId);
    event.setAmountCent(amount);
}, 42L, 9999L);

// ④ 三参 / 任意参:用 EventTranslatorVararg
rb.publishEvent((event, sequence, args) -> {
    event.setOrderId((Long) args[0]);
    event.setUserId((Long) args[1]);
}, 1001L, 42L);

// ⑤ 非阻塞发布:缓冲区满时返回 false,由调用方决定是丢弃还是降级
boolean ok = rb.tryPublishEvent((event, sequence, id) -> event.setOrderId(id), 1001L);
if (!ok)
{
    // 降级:落 DB / 落本地文件 / 直接丢弃
}
方法 满时行为 适用
publishEvent(...) 自旋等待直到有空位 不能丢数据的场景
tryPublishEvent(...) 立即返回 false 可降级、可丢弃的场景(如埋点、行情快照)

💡 更好的写法:把 EventTranslatorOneArg 提取为静态常量或枚举单例,避免每次调用都创建一个 lambda 实例(虽然现代 JVM 会缓存无捕获 lambda,但捕获型 lambda 每次都是新对象):

java 复制代码
private static final EventTranslatorOneArg<OrderEvent, Long> ORDER_TRANSLATOR =
        (event, sequence, orderId) -> { event.setOrderId(orderId); };
// 调用
ringBuffer.publishEvent(ORDER_TRANSLATOR, 1001L);

8.5 Spring Boot 集成

① 线程工厂(必加,否则线上排查时线程名全是 pool-N-thread-M)

java 复制代码
package com.example.disruptor.config;

import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

public class NamedThreadFactory implements ThreadFactory
{
    private final String prefix;
    private final AtomicInteger counter = new AtomicInteger(1);
    private final boolean daemon;

    public NamedThreadFactory(String prefix)
    {
        this(prefix, true);
    }

    public NamedThreadFactory(String prefix, boolean daemon)
    {
        this.prefix = prefix;
        this.daemon = daemon;
    }

    @Override
    public Thread newThread(Runnable r)
    {
        Thread t = new Thread(r, prefix + "-" + counter.getAndIncrement());
        t.setDaemon(daemon);                 // 守护线程:不阻塞 JVM 退出
        t.setUncaughtExceptionHandler((thread, ex) ->
                System.err.println("[" + thread.getName() + "] 未捕获异常: " + ex));
        return t;
    }
}

② 异常处理器(生产环境必备,替代默认的 FatalExceptionHandler

java 复制代码
package com.example.disruptor.config;

import com.lmax.disruptor.ExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 记录日志但不中断消费线程 ------ 默认的 FatalExceptionHandler 会让线程直接死掉
 */
public class LogAndContinueExceptionHandler<T> implements ExceptionHandler<T>
{
    private static final Logger log = LoggerFactory.getLogger(LogAndContinueExceptionHandler.class);

    @Override
    public void handleEventException(Throwable ex, long sequence, T event)
    {
        // 注意:这里要快速返回,不要在消费线程里做重活(写 DB / 发 MQ)
        log.error("处理事件异常 seq={} event={}", sequence, event, ex);
    }

    @Override
    public void handleOnStartException(Throwable ex)
    {
        log.error("EventHandler onStart 异常", ex);
    }

    @Override
    public void handleOnShutdownException(Throwable ex)
    {
        log.error("EventHandler onShutdown 异常", ex);
    }
}

③ 配置类

java 复制代码
package com.example.disruptor.config;

import com.example.disruptor.order.AuditEventHandler;
import com.example.disruptor.order.NotifyEventHandler;
import com.example.disruptor.order.OrderEvent;
import com.example.disruptor.order.RiskEventHandler;
import com.example.disruptor.order.StoreEventHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.SleepingWaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DisruptorConfig
{
    private static final int BUFFER_SIZE = 1 << 14;   // 16384

    /**
     * destroyMethod = "shutdown" 会在容器关闭时调用 Disruptor#shutdown()
     * ⚠️ 这只是"立即停机",不会等待积压消费完;
     *    要优雅停机请用 @PreDestroy 手动调用带超时的 shutdown(见 OrderEventPublisher)
     */
    @Bean(destroyMethod = "")
    public Disruptor<OrderEvent> orderDisruptor()
    {
        Disruptor<OrderEvent> disruptor = new Disruptor<>(
                OrderEvent::new,
                BUFFER_SIZE,
                new NamedThreadFactory("order-disruptor"),
                ProducerType.MULTI,
                new SleepingWaitStrategy());

        disruptor.setDefaultExceptionHandler(new LogAndContinueExceptionHandler<>());

        disruptor.handleEventsWith(new RiskEventHandler(), new AuditEventHandler())
                 .then(new StoreEventHandler())
                 .then(new NotifyEventHandler());

        disruptor.start();
        return disruptor;
    }

    @Bean
    public RingBuffer<OrderEvent> orderRingBuffer(Disruptor<OrderEvent> orderDisruptor)
    {
        return orderDisruptor.getRingBuffer();
    }
}

④ 发布服务(含优雅停机)

java 复制代码
package com.example.disruptor.order;

import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import jakarta.annotation.PreDestroy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

@Service
public class OrderEventPublisher
{
    private static final Logger log = LoggerFactory.getLogger(OrderEventPublisher.class);

    private final RingBuffer<OrderEvent> ringBuffer;
    private final Disruptor<OrderEvent> disruptor;

    /** 复用同一个 Translator 实例,避免重复创建 */
    private static final com.lmax.disruptor.EventTranslatorOneArg<OrderEvent, OrderRequest>
            REQUEST_TRANSLATOR = (event, sequence, req) ->
    {
        event.reset();
        event.setOrderId(req.orderId());
        event.setUserId(req.userId());
        event.setAmountCent(req.amountCent());
        event.setProductCode(req.productCode());
    };

    public OrderEventPublisher(RingBuffer<OrderEvent> ringBuffer, Disruptor<OrderEvent> disruptor)
    {
        this.ringBuffer = ringBuffer;
        this.disruptor = disruptor;
    }

    /** 发布订单事件(阻塞式,缓冲区满会自旋等待) */
    public void publish(OrderRequest request)
    {
        ringBuffer.publishEvent(REQUEST_TRANSLATOR, request);
    }

    /** 尝试发布(非阻塞),适合可降级场景 */
    public boolean tryPublish(OrderRequest request)
    {
        boolean ok = ringBuffer.tryPublishEvent(REQUEST_TRANSLATOR, request);
        if (!ok)
        {
            log.warn("RingBuffer 已满,订单 {} 发布失败", request.orderId());
        }
        return ok;
    }

    /**
     * 优雅停机:先停止接收新请求(由 Web 容器负责),
     * 再等待 Disruptor 把积压消费完,最后才关闭
     */
    @PreDestroy
    public void shutdown()
    {
        log.info("开始关闭 Disruptor,剩余待消费事件数 ≈ {}",
                ringBuffer.getCursor() - ringBuffer.getMinimumGatingSequence());
        disruptor.shutdown(30, TimeUnit.SECONDS);   // ⚠️ 4.0 无 shutdown(Duration) 重载
        log.info("Disruptor 已关闭");
    }

    /** 入参 DTO(record 便于写示例,实际项目可换成普通类) */
    public record OrderRequest(long orderId, long userId, long amountCent, String productCode) { }
}

⑤ 使用

java 复制代码
@RestController
@RequestMapping("/api/order")
public class OrderController
{
    private final OrderEventPublisher publisher;

    public OrderController(OrderEventPublisher publisher)
    {
        this.publisher = publisher;
    }

    @PostMapping
    public ResponseEntity<String> create(@RequestBody OrderEventPublisher.OrderRequest req)
    {
        publisher.publish(req);
        return ResponseEntity.ok("accepted");
    }
}

application.yml(配套参数)

yaml 复制代码
disruptor:
  order:
    buffer-size: 16384          # 必须是 2 的幂
    wait-strategy: sleeping     # busy_spin / yielding / sleeping / blocking
    shutdown-timeout-seconds: 30
    producer-type: multi        # single / multi

logging:
  level:
    com.example.disruptor: INFO

容量怎么定? 经验公式:

bufferSize ≥ 峰值 QPS × 消费者单条耗时(秒) × 安全系数(3~5)

例:峰值 5000 QPS,消费耗时 2ms(含 DB 写入),则 5000 × 0.002 × 5 = 50 → 取 64 或 128 即可。

但当容量较小时,生产者的 CAS 自旋和消费者的 waitFor 都会更频繁地"绕圈",性能反而下降------不是越小越好,通常宁大勿小 (内存代价只是 bufferSize × 对象大小)。


8.6 EventPoller:非阻塞拉取(Netty/推送场景)

EventPoller 让消费者不占用一个线程去阻塞等待 ,而是由外部事件循环(如 Netty 的 EventLoop)驱动"有数据就处理"。

java 复制代码
package com.example.disruptor.poll;

import com.example.disruptor.order.OrderEvent;
import com.lmax.disruptor.EventPoller;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.lmax.disruptor.util.DaemonThreadFactory;
import com.lmax.disruptor.BusySpinWaitStrategy;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 场景:把 Disruptor 里的行情/消息推送给长连接客户端。
 * 用 EventPoller 把"消费"变成"轮询",可以挂在 Netty EventLoop 上,
 * 不为消费者单独占用阻塞线程。
 */
public class EventPollerDemo
{
    public static void main(String[] args) throws Exception
    {
        Disruptor<OrderEvent> disruptor = new Disruptor<>(
                OrderEvent::new, 1024, DaemonThreadFactory.INSTANCE,
                ProducerType.MULTI, new BusySpinWaitStrategy());

        // 只启动 RingBuffer,不注册任何阻塞式消费者
        RingBuffer<OrderEvent> ringBuffer = disruptor.start();

        // 创建 Poller(会占用一个 gating sequence,生产者会尊重它的进度)
        EventPoller<OrderEvent> poller = ringBuffer.newPoller();

        // Handler 返回 true 表示"还要继续轮询",返回 false 表示"本轮到此为止"
        EventPoller.Handler<OrderEvent> handler = (event, sequence, endOfBatch) ->
        {
            System.out.println("poll 到 [" + event + "] seq=" + sequence
                    + " endOfBatch=" + endOfBatch);
            return true;      // 继续消费
        };

        // 模拟外部驱动:Netty 场景下应挂在 channelRead / EventLoop 的定时任务上
        ScheduledExecutorService driver = Executors.newSingleThreadScheduledExecutor();
        driver.scheduleWithFixedDelay(() ->
        {
            try
            {
                // ⚠️ poller.poll 不是线程安全的,必须在同一个线程调用
                EventPoller.PollState state = poller.poll(handler);
                // state 取值:PROCESSING_EVENTS(正在处理,说明还没消费完)
                //            IDLE(空闲,无数据)
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }, 0, 1, TimeUnit.MILLISECONDS);

        // 生产数据
        for (long i = 0; i < 20; i++)
        {
            ringBuffer.publishEvent((e, seq) -> { e.reset(); e.setOrderId(seq); });
        }

        Thread.sleep(500);
        driver.shutdown();
        disruptor.shutdown(3, TimeUnit.SECONDS);
    }
}
对比项 BatchEventProcessor EventPoller
驱动方式 独立线程阻塞 waitFor 外部线程主动 poll
线程占用 每消费者 1 线程 复用调用方线程
适用 通用业务异步 与事件循环(Netty/Reactor)集成、需要背压控制
线程安全 各自独立线程,安全 poll() 非线程安全,必须单线程调用
注意 --- 使用 Poller 后不要 再对同一 RingBuffer 调 handleEventsWith 的阻塞消费者(可以,但要理解两者是独立的 gating)

⚠️ Disruptor#start() 在没有注册任何 handler 时也能用------它只是把 RingBuffer 准备好。上面这个例子里 handleEventsWith 完全没调用,只用了 Poller。


8.7 压测代码

最简压测:不用 JMH,直接数吞吐

java 复制代码
package com.example.disruptor.bench;

import com.lmax.disruptor.*;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.lmax.disruptor.util.DaemonThreadFactory;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * Disruptor 吞吐压测:对比不同 WaitStrategy / ProducerType 的表现
 * 运行:java -cp ... BenchDisruptor 10000000 16384 busy_spin multi
 */
public class BenchDisruptor
{
    static final class ValueEvent
    {
        long value;
        public void set(long v) { this.value = v; }
    }

    public static void main(String[] args) throws Exception
    {
        long iterations = args.length > 0 ? Long.parseLong(args[0]) : 10_000_000L;
        int bufferSize   = args.length > 1 ? Integer.parseInt(args[1]) : 16384;
        String wsName    = args.length > 2 ? args[2] : "busy_spin";
        String ptName    = args.length > 3 ? args[3] : "multi";

        WaitStrategy ws = switch (wsName)
        {
            case "busy_spin" -> new BusySpinWaitStrategy();
            case "yielding"  -> new YieldingWaitStrategy();
            case "sleeping"  -> new SleepingWaitStrategy();
            case "blocking"  -> new BlockingWaitStrategy();
            default -> throw new IllegalArgumentException("unknown: " + wsName);
        };
        ProducerType pt = "single".equals(ptName) ? ProducerType.SINGLE : ProducerType.MULTI;

        Disruptor<ValueEvent> disruptor = new Disruptor<>(
                ValueEvent::new, bufferSize, DaemonThreadFactory.INSTANCE, pt, ws);

        AtomicLong consumed = new AtomicLong();
        CountDownLatch done = new CountDownLatch(1);

        disruptor.handleEventsWith((event, sequence, endOfBatch) ->
        {
            if (consumed.incrementAndGet() == iterations)
            {
                done.countDown();
            }
        });
        RingBuffer<ValueEvent> rb = disruptor.start();

        // 预热
        for (int i = 0; i < 100_000; i++)
        {
            rb.publishEvent((e, seq) -> e.set(seq));
        }
        while (consumed.get() < 100_000) { Thread.onSpinWait(); }
        consumed.set(0);

        // 正式压测
        long start = System.nanoTime();
        for (long i = 0; i < iterations; i++)
        {
            rb.publishEvent((e, seq) -> e.set(seq));
        }
        done.await();
        long costNs = System.nanoTime() - start;

        double opsPerSec = iterations * 1_000_000_000.0 / costNs;
        System.out.printf("策略=%s 生产者=%s 容量=%d 条数=%d%n",
                wsName, ptName, bufferSize, iterations);
        System.out.printf("总耗时=%.3f s  吞吐=%.0f ops/s  单条平均=%.1f ns%n",
                costNs / 1e9, opsPerSec, costNs / (double) iterations);

        disruptor.shutdown(5, TimeUnit.SECONDS);
    }
}

运行参数建议(贴近生产环境):

bash 复制代码
java -server \
     -XX:+UseParallelGC \
     -Xms2g -Xmx2g \
     -XX:-RestrictContended \
     -cp target/classes:disruptor-4.0.0.jar \
     com.example.disruptor.bench.BenchDisruptor 10000000 16384 busy_spin multi

输出示例(不同策略对比,同一台机器)

WaitStrategy ProducerType 吞吐 (ops/s) 单条平均 (ns)
blocking multi ~4,500,000 ~220
sleeping multi ~12,000,000 ~83
yielding multi ~22,000,000 ~45
busy_spin multi ~28,000,000 ~36
busy_spin single ~45,000,000 ~22

⚠️ 上表是示意量级(MacBook M 系列 / 单消费者 / 无业务逻辑),你的真实数字取决于 CPU、JVM 参数、事件对象大小、业务耗时 。这里想说明的是相对关系

  • blocking → busy_spin5~6 倍差距;
  • multi → single约 1.5 倍差距(省掉了 CAS);
  • 业务逻辑(一次 DB 写入 ~1ms)一旦加进来,这些差异会被完全淹没------此时更该关注的是消费者并行度和批量化

用 JMH 更严谨(避免 JIT 预热、死代码消除等干扰):

xml 复制代码
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-core</artifactId>
    <version>1.37</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.openjdk.jmh</groupId>
    <artifactId>jmh-generator-annprocess</artifactId>
    <version>1.37</version>
    <scope>test</scope>
</dependency>
java 复制代码
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@Warmup(iterations = 3, time = 2)
@Measurement(iterations = 5, time = 3)
@Fork(1)
@State(Scope.Benchmark)
public class DisruptorJmhBench
{
    private Disruptor<BenchDisruptor.ValueEvent> disruptor;
    private RingBuffer<BenchDisruptor.ValueEvent> ringBuffer;

    @Setup(Level.Trial)
    public void setup()
    {
        disruptor = new Disruptor<>(BenchDisruptor.ValueEvent::new, 16384,
                DaemonThreadFactory.INSTANCE, ProducerType.SINGLE, new BusySpinWaitStrategy());
        disruptor.handleEventsWith((e, seq, endOfBatch) -> { /* 空实现,只测框架开销 */ });
        ringBuffer = disruptor.start();
    }

    @TearDown(Level.Trial)
    public void tearDown() throws Exception
    {
        disruptor.shutdown(5, TimeUnit.SECONDS);
    }

    @Benchmark
    public void publish()
    {
        ringBuffer.publishEvent((e, seq) -> e.set(seq));
    }
}

8.8 4.0 新特性:批次回退 Rewind

适用场景 :下游(如数据库)临时不可用,希望整个批次回退重试,而不是"跳过这一条继续往下"。

三个新增类型 (包名都是 com.lmax.disruptor注意不在 dsl 包下):

java 复制代码
// ① 回退动作:只有两个取值
public enum RewindAction
{
    REWIND,    // 回退到本批次起点,重来
    THROW      // 放弃回退,抛给 ExceptionHandler
}

// ② 回退策略:返回 REWIND 还是 THROW,由你决定
public interface BatchRewindStrategy
{
    RewindAction handleRewindException(RewindableException ex, int attempt);
}

// ③ 可回退的 Handler:onEvent 多声明了一个 RewindableException
public interface RewindableEventHandler<T> extends EventHandlerBase<T>
{
    void onEvent(T event, long sequence, boolean endOfBatch)
            throws RewindableException, Exception;
}

// ④ 触发回退的异常(注意:它直接继承 Throwable,不是 Exception)
public class RewindableException extends Throwable
{
    public RewindableException(Throwable cause) { ... }
}

两种用法

java 复制代码
import com.lmax.disruptor.BatchRewindStrategy;
import com.lmax.disruptor.RewindAction;
import com.lmax.disruptor.RewindableEventHandler;
import com.lmax.disruptor.RewindableException;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;
import com.lmax.disruptor.BlockingWaitStrategy;
import com.lmax.disruptor.RingBuffer;

import java.util.concurrent.TimeUnit;

public class RewindDemo
{
    public static void main(String[] args) throws Exception
    {
        Disruptor<OrderEvent> disruptor = new Disruptor<>(
                OrderEvent::new, 8192, new NamedThreadFactory("order-rewind"),
                ProducerType.MULTI, new BlockingWaitStrategy());

        // 兜底异常处理:回退次数用尽(THROW)后由它接手
        disruptor.setDefaultExceptionHandler(new LogAndContinueExceptionHandler<>());

        // 自定义回退策略:指数退避,最多重来 3 次
        BatchRewindStrategy retry3Times = (rewindableException, attempt) ->
        {
            if (attempt >= 3)
            {
                return RewindAction.THROW;          // 放弃,交给 ExceptionHandler
            }
            try
            {
                Thread.sleep(50L * attempt);        // 退避:50ms / 100ms
            }
            catch (InterruptedException ignored)
            {
                Thread.currentThread().interrupt();
                return RewindAction.THROW;
            }
            return RewindAction.REWIND;             // 整批回退重来
        };

        // ⭐ 用法一:通过 DSL 一次性声明"策略 + 可回退 Handler"
        //    注意参数顺序:策略在前,Handler 在后
        disruptor.handleEventsWith(retry3Times, new StoreEventHandler2());

        disruptor.start();
        RingBuffer<OrderEvent> rb = disruptor.getRingBuffer();

        for (long i = 0; i < 100; i++)
        {
            rb.publishEvent((event, seq) -> { event.reset(); event.setOrderId(seq); });
        }

        disruptor.shutdown(10, TimeUnit.SECONDS);
    }
}

/** 批次回退型 Handler:DB 不可用时整批重来 */
class StoreEventHandler2 implements RewindableEventHandler<OrderEvent>
{
    @Override
    public void onEvent(OrderEvent event, long sequence, boolean endOfBatch)
            throws RewindableException
    {
        if (!databaseAvailable())
        {
            // 抛出后:本批已处理的事件会被"撤回",下次从批次起点重新处理
            throw new RewindableException(new RuntimeException("DB 不可用"));
        }
        // 正常落库
    }

    private boolean databaseAvailable()
    {
        return true;
    }
}

⚠️ 使用前必须理解的三条语义

要点 说明
副作用必须可重放 回退意味着 onEvent 会被重复调用 。如果 Handler 里有"扣款""发短信"这类不可重入的操作,回退会造成重复执行。只把它用在幂等操作上
回退代价与批次大小成正比 批次越大,回退时白做的工作越多。用 BatchEventProcessor 的最大批次大小参数限制单批条数(4.0 已把它变成构造器参数,不再是 setMaxBatchSize() 方法)。
attempt 从 1 开始 别写成 attempt > 3 之外还想叠加计数器------attempt 由框架传入,是唯一的重试依据。

⚠️ 4.0 新增能力 ,3.4.4 完全没有。它的价值在于:把"事务边界"从单条事件提升到整批事件 ------搭配 endOfBatch 使用,可以实现"批内全成功才提交"的效果。若你的场景是"逐条重试"而非"整批重试",用普通的 ExceptionHandler + 自己重投递更合适。


9. 常见坑与 FAQ

9.1 十个必踩的坑

# 后果 正确做法
1 在消费者里保存 event 引用 之后读到的是被覆盖后的新值 立即把需要的数据拷贝到自己的对象里
2 容量写成非 2 的幂(如 1000) 启动时抛 IllegalArgumentException Util.ceilingNextPowerOfTwo(1000) = 1024
3 声明 SINGLE 但多线程发布 静默数据错乱,极难排查 拿不准用 MULTI
4 忘记 event.reset() 上一事件的残留字段污染当前事件 每个事件定义 reset() 并在 translator 里首行调用
5 用默认的 FatalExceptionHandler 一次异常就杀死消费线程,链路彻底 hang 住 自定义 ExceptionHandler 记日志并继续
6 不调 shutdown() 非守护线程阻止 JVM 退出;数据丢失 @PreDestroy 里调 shutdown(timeout, unit)
7 在 Handler 里做耗时操作(HTTP 调用、大事务) 生产者被"卡"------缓冲区很快填满并自旋 拆成多个 Handler 阶段,或交给下游线程池
8 并行分支的 Handler 写同一字段 数据竞争,结果不确定 每个分支只写自己负责的字段
9 start() 后还想改依赖图 checkNotStarted() 抛异常 所有 handleEventsWith 必须在 start() 之前
10 一个 JVM 建几十个 Disruptor 实例 线程数爆掉(每 handler 一线程) 复用实例,或合并到少数几个 RingBuffer

9.2 FAQ

Q1:Disruptor 和 Disruptor 的中文名"干扰器"是一回事吗?它和 Kafka/RocketMQ 是什么关系?

不是一回事。Disruptor 是进程内(In-Process)框架,没有网络、没有持久化、没有分区概念;Kafka/RocketMQ 是分布式 MQ ,天然带网络和持久化。二者是互补的:很多 MQ 的客户端/服务端内部也用 Disruptor 做"网络线程 → 业务线程"的解耦(例如 Log4j2 用 Disruptor 做异步日志,是同一个思路)。

Q2:Disruptor 能替代线程池吗?

不能完全替代,但可以替代**"线程池 + 有界队列"**这一个组合。区别在于:

维度 ThreadPoolExecutor + ArrayBlockingQueue Disruptor
任务模型 每个任务是独立对象 事件对象复用
消费顺序 无序(多线程争抢) 严格按序号,可并行但有序可溯
扇出 一条任务只被一个线程处理 一条事件可被 N 个消费者各处理一遍(广播)
依赖编排 不支持 支持(.then()
拒绝策略 可自定义 tryPublishEvent 返回 false
GC 压力 极低

Q3:消费者抛异常会怎样?怎么自定义?

默认 FatalExceptionHandler 的行为是:打日志 + 包装成 RuntimeException 抛出BatchEventProcessor.run()finally 执行 notifyShutdown()running.set(false)该消费线程彻底退出 。此后 RingBuffer 很快写满,生产者全部自旋,整个链路 hang 住

正确做法(生产环境必做):

java 复制代码
disruptor.setDefaultExceptionHandler(new ExceptionHandler<OrderEvent>()
{
    @Override
    public void handleEventException(Throwable ex, long sequence, OrderEvent event)
    {
        // ① 记日志(含序号,便于用序号回溯数据)
        log.error("处理失败 seq={} event={}", sequence, event, ex);
        // ② 决定是否落库/告警/进死信队列
        deadLetterService.save(sequence, event, ex);
        // ③ 快速返回,不要抛异常,不要做重活
    }

    @Override
    public void handleOnStartException(Throwable ex)
    {
        log.error("onStart 失败", ex);
    }

    @Override
    public void handleOnShutdownException(Throwable ex)
    {
        log.error("onShutdown 失败", ex);
    }
});

注意 BatchEventProcessor 的异常分支会执行 sequence.set(nextSequence); nextSequence++;------跳过这条事件继续往下,所以自定义 Handler 不抛出,就能保证链路存活。

Q4:为什么我换了 WaitStrategy 性能没变化?

三个常见原因:

  1. 生产者成了瓶颈 (见 5.5 细节 B)------RingBuffer 满时走的是硬编码的 LockSupport.parkNanos(1),与 WaitStrategy 无关。给足容量、加快消费者才能解决。
  2. 业务耗时淹没了框架开销 ------一次 DB 写入 1ms,框架省下的 50ns 毫无意义。此时应该做的是加消费者并行度批量化 DB 写入
  3. CPU 核数不够 ------BusySpinWaitStrategy 需要独占核;如果消费者数 ≥ 核数,自旋反而抢占业务线程,性能下降。

Q5:RingBuffer 的容量到底设多大?

三条约束:

  1. 必须是 2 的幂(否则启动抛异常);
  2. 不小于"最大允许积压量"峰值QPS × 消费者单条耗时 × 安全系数
  3. 别太小:容量过小会导致生产者频繁 "绕圈等待",性能陡降。

经验值:1024 ~ 65536 。内存代价 = bufferSize × 事件对象大小(16K × 200B ≈ 3MB,完全可以接受)。

Q6:如何监控 Disruptor 的运行状态?

四个关键指标:

java 复制代码
RingBuffer<OrderEvent> rb = disruptor.getRingBuffer();

long cursor = rb.getCursor();                                   // 生产者已发布到哪
long minGating = rb.getMinimumGatingSequence();                 // 最慢消费者追到哪
long backlog = cursor - minGating;                              // ⭐ 积压量
double usage = (double) backlog / rb.getBufferSize();           // ⭐ 使用率

// 各消费者的进度(需要自己持有 Sequence 引用)
// 注意:Sequence 是在 handleEventsWith 时创建的,请提前保存引用

推荐用 Micrometer 暴露:

java 复制代码
@Component
public class DisruptorMetrics
{
    private final RingBuffer<OrderEvent> ringBuffer;
    private final MeterRegistry registry;

    public DisruptorMetrics(RingBuffer<OrderEvent> ringBuffer, MeterRegistry registry)
    {
        this.ringBuffer = ringBuffer;
        this.registry = registry;

        // 背压水位:> 80% 就该告警
        Gauge.builder("disruptor.order.backlog.ratio", ringBuffer, rb ->
                {
                    long backlog = rb.getCursor() - rb.getMinimumGatingSequence();
                    return (double) backlog / rb.getBufferSize();
                })
             .description("Disruptor 缓冲区积压比例")
             .register(registry);
    }
}

告警阈值建议:积压率 > 0.8 持续 30 秒 → 说明消费能力不足,需要扩容或优化消费者。

Q7:Disruptor 支持"一条消息只被一个消费者处理"(竞争消费)吗?

4.0 起不支持 。3.x 有 WorkerPool / WorkProcessorhandleEventsWithWorkerPool,4.0 因从未在 LMAX 内部使用而被移除。若需要该语义,两种替代方案:

  1. 单消费者 + 内部分发 :一个 BatchEventProcessor 消费,内部按 key hash 派发到多个线程池(注意会引入新的队列);
  2. 自实现 WorkerPool :多个 BatchEventProcessor 共享同一个 Sequence,谁抢到谁处理(需要自己处理序号推进的原子性)。

Q8:shutdown()halt() 有什么区别?

方法 行为 用在哪
disruptor.shutdown() 先等积压消费完 (轮询 hasBacklog()),再逐个 halt() 优雅停机
disruptor.shutdown(timeout, unit) 同上,但等待上限 timeout 推荐,避免卡死
disruptor.halt() 立即停止所有消费者,未消费的事件直接丢弃 紧急停止、测试清理
batchEventProcessor.halt() 立即停止单个消费者 精细控制

⚠️ shutdown() 的"等待积压"依赖 ringBuffer.getCursor() 与所有消费者 getSequence() 的比较。如果你的 Handler 里有死循环,shutdown()永远等下去------所以务必带上 timeout。

Q9:为什么消费者里拿到的 endOfBatch 总是不对/总是 true?

endOfBatch = (sequence == availableSequence)。在低负载 场景下,生产者发一条、消费者立刻处理一条,availableSequence == nextSequence 恒成立,所以每条都是 endOfBatch = true。这不是 bug。高负载时才会出现"一批多条"。

所以:不要把业务逻辑正确性依赖于 endOfBatch 的批次大小假设,它只应该用于"能批量就批量"的性能优化(如批量 flush)。

Q10:Disruptor 与 nettyMpscQueue 该如何选?

维度 Disruptor JCTools MpscArrayQueue
消费模型 严格有序、支持依赖图、支持广播 无序、单消费者
批量 支持 支持(drain
内存占用 高(padding + availableBuffer)
延迟 极低
适用 需要多消费者编排、广播 简单的"多生产者 → 单消费者"桥接

若你只是要把 Netty 的 IO 线程数据交给一个业务线程,MpscArrayQueue 更轻;若要做"风控 + 审计 + 落库"的多级编排,Disruptor 无可替代。参见 \[netty知识梳理-使用kimi3]。


10. 参考资料

官方资源

版本与依赖


上手路径

  1. 跑通 8.2 的 Hello World,把 WaitStrategy 换成四种各跑一遍,观察 CPU 占用和吞吐差异;
  2. 把项目里一个"线程池 + 有界队列"的异步链路改成 Disruptor(先只用一个消费者),加上自定义 ExceptionHandler 和积压监控;
  3. 用 8.3 的菱形依赖图改造多级处理链路,再按 8.7 做一轮真实业务耗时下的压测------你会发现在有 DB I/O 的场景,瓶颈从来不在 Disruptor 上
  4. MultiProducerSequencerSequence 的完整源码(不到 400 行),亲手写一个简化版 RingBuffer,是理解无锁编程最有效的练习。

一句话记住 Disruptor它把"队列"从"需要加锁的数据结构"重新定义成了"一组单调递增的序号 + 一块预分配的内存"。 理解了这句话,就理解了它为什么快。

相关推荐
Lyyaoo.2 小时前
【二分查找】【困难】寻找两个有序数组的中位数
java·数据结构·算法
小羊没烦恼!2 小时前
Memory 记忆设计讨论:Agent Memory 的安全边界:权限、删除、版本与回执
java·大数据·word·powerpoint·.net
这是程序猿2 小时前
Java线程池深度剖析:源码原理、核心参数、任务调度与生产调优实战
java·开发语言·python
rannn_1112 小时前
【Java面试八股】Java基础篇|数据类型、面向对象、关键字、反射、注解、异常、新特性、序列化、设计模式、IO
java·后端·面试
MacroZheng2 小时前
阿里又开源了一个神级 Skill 项目!
java·人工智能·后端
修罗-zero3 小时前
UI自动化开发(selenium):模拟人操作浏览器
java·selenium自动化
ruleslol3 小时前
Function Calling 讲解
java·ai
小小猪的春天3 小时前
MCP 半小时跑通和敢上生产之间,隔着这 5 个坑
java·ai编程
步行cgn3 小时前
Spring 注入 Properties 详解
java·python·spring