核心操作类BoundedLocalCache
1 |
|
这是一个get方法,先看get方法干了什么。
1 |
|
MPSC 模型
1. 并发原语层——索引与标记
producerIndex / consumerIndex
volatile 长整形,偶数存序号、奇数当扩容锁,一条 CAS 完成“抢槽位 + 加锁”。
producerLimit
由消费者提前算好并发布的水位线,生产者只需volatile 读即可无锁判断“快满”。
2. 数据存储层——数组 + 链表
chunk 数组(2 的幂)
真正存元素;末尾留 1 个槽放JUMP对象,表示“后面还有新数组”。
单向链表
旧数组buffer[last]指向新数组,实现无界扩展,消费者顺着链表继续消费。
3. 伪共享隔离层——冷热字段垫片
120 byte 填充段(15×8 byte)
把producerIndex / consumerIndex等热变量推到独占缓存行,避免多核互相失效。
继承链分段
冷字段 → 垫 → 热字段 → 垫,Java 字段排列即可跨 64 B 边界,零注解、零 Unsafe。
4. 无锁算法层——CAS + Ordered Store
offer
CAS 抢producerIndex→ Ordered store 写元素 → 槽满则自旋扩容。
poll
plain 读本地快照 → Ordered load 拿元素 → 消费完一批更新水位producerLimit。
5. 扩容协调层——单向链表 + JUMP 标记
扩容线程原子置奇数当锁,生产者自旋;
新数组链接到旧数组尾,JUMP 标记提醒消费者跳船到新块;
旧数组完全消费后标注
BUFFER_CONSUMED,后续可被 GC。
一眼看全景
Table
Copy
| 层级 | 关键实体 | 作用 |
|---|---|---|
| 并发原语 | *Index / *Limit |
无锁抢槽、水位预告 |
| 存储结构 | chunk[] + 链表 | 可无限扩展的环形缓冲 |
| 隔离优化 | 120 byte 垫片 | 跨缓存行,防伪共享 |
| 算法流程 | CAS + Ordered | 读写均无锁、顺序一致 |
| 扩容协调 | JUMP + 单向链表 | 在线扩容,消费者透明 |
把这五层拼在一起,就是 JCTools MpscUnboundedArrayQueue 能在 4~6 ns 内完成一次 offer/poll 的底层原因
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 无知的小狼!
评论
