<p>RingBuffer 高性能的关键在于 CAS 与 2 的幂次方容量下的位运算协同:容量为 2ⁿ 时,索引取模用 index & (capacity - 1) 替代 % 运算,提速 3.7 倍;CAS 保障游标更新原子性,配合位运算定位下标,实现无锁、非阻塞、低延迟的并发访问。</p>

Java 中 CAS 机制与位运算协同,是实现高性能无锁环形队列(RingBuffer)的关键组合。它不靠锁排队,而是靠原子操作 + 数学优化,把并发访问的开销压到最低。
为什么 RingBuffer 大小必须是 2 的幂次方
这不是约定俗成,而是为位运算服务的硬性设计:
- 当缓冲区容量为 1024(即 2¹⁰)时,索引取模可简化为:
index & (capacity - 1) - 比如
1023的二进制是1111111111(10 个 1),任何数和它做按位与,结果自然落在[0, 1023]范围内 - 相比传统
% capacity(涉及除法指令),位运算在 CPU 上几乎一个周期完成,实测快 3.7 倍
CAS 如何配合位运算保障线程安全
位运算只解决“算得快”,CAS 解决“写得准”。两者结合才能真正无锁:
- 生产者通过 CAS 尝试更新游标(如
cursor),期望值是当前读到的旧值,新值是old + 1 - 成功后,用
newCursor & (bufferSize - 1)算出实际数组下标,写入数据 - 消费者同理:先 CAS 获取下一个可消费位置,再用位运算定位数组元素
- 整个过程没有
synchronized或Lock,失败就重试,不阻塞线程
避免常见陷阱:位运算 + CAS 不等于万能
看似简洁,但几个细节不注意就会出错:
立即学习“Java免费学习笔记(深入)”;
-
缓冲区必须预分配且不可变:数组一旦创建就不能扩容,否则
bufferSize - 1会失效 - 游标变量必须 volatile 或 AtomicXXX:确保多核 CPU 下的可见性,否则 CAS 可能基于过期值判断
-
不能直接用 int 自增替代 CAS:比如
i++非原子,AtomicInteger.incrementAndGet()才是底层调用 CAS 的安全封装 - 注意 ABA 问题:比如游标从 5→1000→5,CAS 会误判为未变;Disruptor 用序列号递增+严格单调性规避,而非依赖值本身
一个典型片段示意(简化版)
假设 buffer 是预分配的 Object[],cursor 是 AtomicLong:
// 生产者尝试发布一个事件 long next = cursor.incrementAndGet(); // CAS 自增,返回新值 int index = (int)(next & (buffer.length - 1)); // 位运算得下标 buffer[index] = event; // 写入(此处通常还伴随内存屏障或 lazySet)
这行 next & (buffer.length - 1) 就是位运算技巧的落地点——它轻量、确定、零分支,和 CAS 形成高效闭环。


















