PriorityBlockingQueue 是固定容量的无界队列,底层用可扩容Object[]实现;它没有allocationSpinLock字段,扩容由synchronized块保护,非无锁,在offer()中加锁执行,不支持并发扩容。

,它是一个**固定容量的无界队列**(unbounded),但其底层使用的是可动态增长的 Object[] 数组,表面上“无界”,实则通过数组扩容来实现容量伸缩。然而,**它没有 `allocationSpinLock` 这个字段,也不使用该机制实现扩容**——这是对 JDK 源码的常见误解。 下面澄清关键事实,并准确分析其扩容逻辑与并发行为:
1. PriorityBlockingQueue 没有 allocationSpinLock
`allocationSpinLock` 是 ConcurrentHashMap(JDK 7)或某些旧版并发容器中用于控制初始化/扩容竞争的自旋锁变量。而 PriorityBlockingQueue 的源码(JDK 8–21)中完全不存在该字段。它的扩容操作由普通 synchronized 块保护,不依赖自旋锁,更不是“无锁”。
2. 扩容发生在 offer() 中,且是加锁的、单线程执行的
当队列满(即 size == queue.length)时,offer(E) 会触发扩容:
- 调用
tryGrow(Object[], int)方法(JDK 8+) - 该方法先释放当前持有的全局锁(ReentrantLock),再用 synchronized(this) 对队列实例加锁
- 在 synchronized 块内完成数组复制与扩容(通常扩大 1.5 倍)
- 扩容完成后,重新获取 ReentrantLock,继续入队流程
这意味着:扩容本身是互斥的、阻塞式的、非无锁的,只是为避免锁竞争过久,临时释放主锁转用对象锁做扩容。
3. 并发下的实际行为:扩容不并发,但插入可并发(除扩容时刻)
- 正常插入(size
- 触发扩容时:所有线程在 tryGrow 中短暂释放主锁 → 竞争 synchronized(this) → 只有一个线程能进入扩容,其余阻塞等待
- 扩容完成后,其他线程继续尝试 offer,可能再次触发扩容(若仍满),但仍是串行化处理
因此,它并非“利用 allocationSpinLock 实现无锁扩容”,而是用锁降级(lock downgrading)策略缓解扩容期间的锁争用。
4. 为什么容易误传“allocationSpinLock”?
该误解可能源于:
- 混淆了 PriorityBlockingQueue 与 ConcurrentLinkedQueue / ConcurrentHashMap 的实现细节
- 将 “allocation spin lock” 泛化理解为任意扩容自旋机制,但 JDK 中并未如此命名或实现
- 部分博客或面试资料未核查源码,沿用错误术语
真实源码路径(JDK 17):java.util.concurrent.PriorityBlockingQueue.tryGrow() —— 可见 synchronized(this) 和 Arrays.copyOf。











