一、为什么需要 Disruptor?—— 背景与问题
在高并发编程中,传统的队列(如 或 )在高性能场景下会成为瓶颈,主要问题在于:
Disruptor 是一种基于环形缓冲区的高性能无锁队列实现。它通过预分配内存消除 GC 压力,利用 CAS 和内存屏障实现无锁并发,并通过缓存行填充解决伪共享问题。核心组件包括环形缓冲区、序列、序列屏障、等待策略和事件处理器。支持单/多生产者及消费者依赖关系,适用于金融交易等低延迟场景。

在高并发编程中,传统的队列(如 或 )在高性能场景下会成为瓶颈,主要问题在于:
java.util.concurrent.ArrayBlockingQueueLinkedBlockingQueueDisruptor 的目标就是解决这些问题,实现极低延迟、超高吞吐的线程间数据交换。
Disruptor 不是一个传统意义上的 FIFO 队列,而是一个基于数组的环形缓冲区(Ring Buffer)。它的核心设计思想可以概括为以下几点:

Event)在初始化时就全部创建好,并被重复使用。这消除了 GC 压力。cursor,各个消费者的 Sequence)。padding),确保每个核心变量独占一个完整的 CPU 缓存行(通常为 64 字节),防止它们被意外地加载到同一个缓存行中,从而避免一个线程的写入使另一个线程的整个缓存行失效。A->B->C 或 A,B 都完成 -> C),实现高效的工作流。这是 Disruptor 的物理存储核心。它是一个固定大小的 Object[] 数组。每个位置被称为一个'槽'(slot)。
size:必须是 2 的幂次(如 1024)。这样 sequence % size 可以通过 sequence & (size - 1) 位运算高效完成。cursor:生产者发布事件的序列号。它代表最后成功发布的事件的位置。这是一个 Sequence 对象。Sequence 共同决定。Disruptor 的灵魂。它是一个使用 padding 封装的长整型(long)值。
Sequence:
Ring Buffer 有 cursor(一个 Sequence)。EventProcessor(消费者)有自己的 Sequence,表示自己已处理完成的位置。Producer(如果是多生产者)也有自己的 Sequence。Sequence 的值单调递增,代表对应组件在环形缓冲区中的位置。Sequence 的值,就能知道生产和消费的进度关系。消费者用来协调工作、控制进度的核心工具。
cursor 引用。Sequence 引用(用于构建依赖图)。SequenceBarrier:'我可以安全消费的下一个事件是什么?'SequenceBarrier 的逻辑是:返回 min(生产者 cursor, 所有依赖的消费者的 Sequence)。这确保了消费者不会超越其依赖者,从而实现了无锁的有序消费。定义了消费者如何等待新事件的到来。这是影响延迟和 CPU 占用的关键。
BlockingWaitStrategy:使用锁和条件变量。最节省 CPU,但延迟最高。适用于异步日志等场景。**SleepingWaitStrategy**:先自旋,后 Thread.yield(),最后使用 LockSupport.parkNanos(1)。平衡延迟和 CPU。**YieldingWaitStrategy**:先自旋 100 次,然后调用 Thread.yield()。延迟低,但会占用较多 CPU。适用于要求极高吞吐、线程数小于 CPU 核心数的场景。BusySpinWaitStrategy:纯自旋。延迟最低,但疯狂消耗 CPU。必须在绑定核心、线程数少于物理核心数的场景下使用。消费者的执行体。通常指 BatchEventProcessor。
run() 方法内部是一个循环:
SequenceBarrier.waitFor(nextSequence) 等待自己可用的最大 nextSequence。availableSequence 后,从自己的当前 sequence 到 availableSequence 批量处理事件。EventHandler.onEvent() 处理每个事件。Sequence 值。负责向 Ring Buffer 发布事件。分为单生产者(Single Producer) 和多生产者(Multi Producer) 两种模式。
nextSequence = cursor + 1(无竞争,无需 CAS)。nextSequence。nextSequence 对应的 slot。RingBuffer.publish(sequence)。publish 方法会先添加内存屏障(store-store barrier,确保数据写入先于 cursor 更新),然后将 cursor 更新到 sequence。cursor 的更新会通知所有在 SequenceBarrier 上等待的消费者。cursor = -1。Sequence = -1。A。它申请下一个位置:next = cursor + 1 = 0。A 的数据写入 RingBuffer[0 & 7],即 RingBuffer[0]。publish(0),更新 cursor = 0。BatchEventProcessor)在循环中调用 SequenceBarrier.waitFor(0)。SequenceBarrier 发现 cursor (0) >= 0,且没有依赖者,于是返回 availableSequence = 0。sequence (-1) < availableSequence (0),于是处理 RingBuffer[0] 的事件 A。Sequence 更新为 0。B 到 slot 1,更新 cursor=1。消费者等待并处理,如此往复。这是 Disruptor 最强大的部分。例如,我们有三个消费者:C1(数据持久化),C2(数据统计),C3(发送消息,必须在 C1 和 C2 都完成后进行)。
C3 的 SequenceBarrier 会持有 RingBuffer.cursor、C1.sequence 和 C2.sequence。C3 调用 waitFor 时,SequenceBarrier 返回的是 min(生产者 cursor, C1.sequence, C2.sequence)。10,但只要 C1 才处理到 5,C3 最多也只能拿到 5。这样就保证了 C3 不会跑到 C1 前面去,完全无锁地实现了依赖。构建依赖图:
RingBuffer -> C1 -> C2 -> C3 (依赖 C1 和 C2)
Disruptor 本质上是一种精心设计的内存队列,它将共享变量的数量降到最低(核心就是那几个 Sequence),并通过硬件友好的方式(缓存行填充、内存屏障)来操作它们,从而在软件层面最大限度地压榨出现代 CPU 和内存子系统的性能。它特别适用于金融交易、高频计算、事件溯源等对延迟和吞吐有极端要求的领域。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online