RingBuffer 性能关键在避免伪共享,需用@Contended填充热点字段;publish()前必须完成数据写入,且setCursor()由其自动触发;单生产者应选SingleProducerSequencer以避免冗余内存屏障;waitFor()返回负值是中断信号而非错误。

RingBuffer 为什么必须做缓存行填充(@Contended)
Disruptor 的 RingBuffer 性能关键不在“环形”,而在避免伪共享——多个线程频繁读写同一缓存行(64 字节)时,CPU 缓存一致性协议会反复使其他核心的缓存副本失效,造成严重性能抖动。即使你用的是单生产者单消费者,RingBuffer 里 cursor 和 gatingSequences 等字段若挤在同一缓存行,照样被拖垮。
实操建议:
- Java 8+ 必须开启
-XX:+UseCondensedHeaders或更关键的是-XX:-RestrictContended(JDK 9+ 默认开启),否则@sun.misc.Contended注解无效 - Disruptor 3.4.4+ 已在
Sequence类上加了@Contended,但自定义的Event类如果含 long/int 字段且被多线程访问,仍需手动填充 - 不要自己写 7 个
long填充字段——用PaddingLong这类现成的填充字段(Disruptor 源码里就有),语义清晰且 JVM 更易优化 - 验证是否生效:用 JOL(Java Object Layout)工具跑
ClassLayout.parseClass(YourEvent.class).toPrintable(),确认热点字段之间间隔 ≥64 字节
setCursor() 和 publish() 的调用时机与顺序不能颠倒
很多人以为 publish(sequence) 就是“提交”,其实它只是通知消费者:这个位置的数据已就绪;而 setCursor() 是生产者推进自己的写指针。Disruptor 要求严格顺序:先填数据 → 再 publish() → 最后(隐式或显式)setCursor()。错序会导致消费者看到未初始化的事件对象,或死锁在 waitFor()。
常见错误现象:
立即学习“Java免费学习笔记(深入)”;
- 消费者拿到的
Event字段全是默认值(0、null),尤其在高并发下偶发 -
BatchEventProcessor卡住,sequence.get() == -1长时间不更新 - 用
tryNext()分配成功,但没调publish()就 return,后续再无新事件
正确姿势:
long sequence = ringBuffer.next();
try {
Event event = ringBuffer.get(sequence);
event.setValue(data); // ✅ 数据写入必须在 publish 前
} finally {
ringBuffer.publish(sequence); // ✅ publish 是提交动作,不可省略
}
// setCursor() 由 publish() 内部自动完成,无需手动调</p>
<H3>MultiProducerSequencer 和 SingleProducerSequencer 的内存屏障差异</H3>
<p>选错 sequencer 类型不会报错,但会默默引入不必要的 <code>volatile</code> 写和 full memory barrier,吞掉 20%~40% 吞吐量。核心区别在于:单生产者场景下,<code>SingleProducerSequencer</code> 完全不用 CAS 和 volatile 写,只靠 CPU store-store 重排序约束;而 <code>MultiProducerSequencer</code> 每次 <code>next()</code> 都要 CAS 更新 <code>cursor</code>,代价高得多。</p><div class="aritcle_card flexRow">
<div class="artcardd flexRow">
<a class="aritcle_card_img" href="/ai/1410" title="Seed-Music"><img
src="https://img.php.cn/upload/ai_manual/000/000/000/175680059259508.png" alt="Seed-Music" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a href="/ai/1410" title="Seed-Music">Seed-Music</a>
<p>字节跳动推出的AI音乐生成与编辑工具</p>
</div>
<a href="/ai/1410" title="Seed-Music" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a>
</div>
</div>
<p>使用场景判断要点:</p>
<ul>
<li>一个线程调用 <code>ringBuffer.next()</code> → 用 <code>SingleProducerSequencer</code></li>
<li>多个线程各自持有独立 <code>RingBuffer</code> 实例 → 仍是单生产者,不是多生产者</li>
<li>多个线程共用同一个 <code>RingBuffer</code> 并都调 <code>next()</code> → 才需要 <code>MultiProducerSequencer</code></li>
<li>别被“多消费者”误导:消费者数量不影响 sequencer 类型选择,只影响 gating logic</li>
</ul>
<p>性能影响示例:在 3.2GHz CPU 上,单生产者模式下 <code>next()/publish()</code> 延迟可压到 ~15ns;换成多生产者,直接跳到 ~40ns 以上,且随核数增加恶化</p>
<H3>waitFor() 返回值为负数时的真实含义</H3>
<p><code>waitFor(long sequence, Sequence dependentSequence, SequenceBarrier barrier)</code> 返回负值(如 <code>-1</code>、<code>-2</code>)不是 bug,而是 Disruptor 的中断/超时信号机制。它不抛异常,也不代表失败,只是告诉你“当前等不到,你自己决定下一步”。很多人直接把返回值当序列号用,结果逻辑全乱。</p>
<p>典型误用:</p>
<ul>
<li>把 <code>waitFor()</code> 返回值强制转成 <code>long</code> 当事件序号取数据 → 取到内存垃圾</li>
<li>看到负数就 throw new RuntimeException("timeout") → 中断处理被粗暴覆盖</li>
<li>忽略 <code>dependentSequence</code> 的作用,在多消费者依赖链中导致序列错乱</li>
</ul>
<p>正确响应方式:</p>
<pre class="brush:php;toolbar:false;">try {
long availableSeq = barrier.waitFor(nextSequence);
if (availableSeq < 0) {
// 检查是否中断:Thread.interrupted()
// 或是否超时:看你的 timeout 逻辑怎么嵌
continue; // 或 break,取决于业务
}
// ✅ 此时 availableSeq 才是安全可用的序列号
Event event = ringBuffer.get(availableSeq);
handler.onEvent(event, availableSeq, availableSeq == nextSequence);
nextSequence = availableSeq + 1;
} catch (AlertException e) {
// 外部调用了 ringBuffer.alert(),需清理并退出
}
容易被忽略的是:waitFor() 的负返回值具体含义取决于你用的 WaitStrategy 实现——BusySpinWaitStrategy 永远不返回负数,而 TimeoutBlockingWaitStrategy 会在超时时返回 -1。别硬编码判断逻辑。








