You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SPSC场景下自定义SpscArrayQueue的线程安全问题咨询

SPSC队列线程安全问题解答

你当前实现的SpscArrayQueue不是线程安全的,两个疑问点都需要做可见性、有序性保障,具体说明如下:

1. takeIndex与putIndex是否需要定义为AtomicLong类型?

不需要用全功能的AtomicLong,但不能用普通long类型,原因如下:

  • 可见性问题:普通long的写入没有内存屏障保障,生产者更新putIndex后消费者可能永远看不到新值,反过来消费者更新takeIndex后生产者也可能看不到,最终会出现生产者永远认为队列满、消费者永远认为队列空的假死问题。
  • 原子性问题:32位JVM下long类型的读写不具备原子性,可能出现「字撕裂」,读取到半个更新的索引值,直接导致数组越界等异常。
  • 有序性问题:普通变量的读写可能被JVM指令重排,比如putIndex更新的操作可能被排到数组元素写入操作之前,导致消费者读到新索引后拿到空数组元素,造成数据丢失。

正确的做法有两种:

  • 简单实现:给两个索引加volatile修饰即可,因为SPSC场景下两个索引分别只有一个线程写,不需要CAS原子操作,只要保障可见性和有序性就足够。
  • 高性能实现:用AtomicLong存储索引,写入时调用lazySet方法,比volatile写入少一次内存屏障,性能更高。

2. Object[] arr是否需要替换为AtomicReferenceArray?

需要替换,普通对象数组的元素读写不具备volatile语义:

  • 生产者往数组写入元素后,消费者可能读到旧值(null),哪怕生产者已经完成写入操作,会导致队列有数据但消费者读不到。
  • 消费者读取元素后把对应位置设为null的操作,生产者也可能看不到,导致队列有空位但生产者认为队列满无法写入。
  • 同样存在指令重排风险,数组元素的写入和索引更新的顺序可能被打乱,引发数据异常。

如果追求极致性能,也可以不用AtomicReferenceArray,直接用Unsafe的putObjectVolatile、getObjectVolatile方法操作数组元素,和原子数组效果一致,但可以省去包装类的额外开销,JCTools、Disruptor等高性能开源框架的SPSC队列都是这么实现的。

额外说明

你之前认为普通数组只会导致消费延迟、不会有异常行为的认知是错误的:没有可见性和有序性保障的情况下,极端场景下会出现队列完全不可用、数据永久丢失、运行时异常等严重问题,远不止延迟这么简单。

内容的提问来源于stack exchange,提问作者zysaaa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 05:06:01