bigywater.tech
Java · 170

BlockingQueue:生产者消费者如何阻塞与唤醒

生产者消费者怎么阻塞、怎么唤醒?不同队列有什么区别?这篇把每个 API 在队列满或空时的行为和线程状态打印出来,再比较五种队列的吞吐(线程池里队列的作用见线程池一篇)。

WAITING
put 在满队列上、take 在空队列上阻塞时的线程状态
0.41 vs 25.0
SynchronousQueue 与 LinkedTransferQueue 的吞吐(百万次/秒,1 个生产者)
POISON
毒丸:用约定的对象通知消费者退出

满与空的 API 行为

实测 · 本机执行输出
  容量 2 的队列已满:offer(3) = false,offer(3, 100 ms) = false,remainingCapacity = 0
  add(3) 抛 IllegalStateException: Queue full
  put(3) 在队列满时阻塞:生产者线程状态 = WAITING,栈顶 = Unsafe.park ← LockSupport.park ← AbstractQueuedSynchronizer$ConditionNode.block ← ForkJoinPool.unmanagedBlock
  200 ms 后消费者 take() 一个,阻塞的 put 随即完成,put 总共阻塞了约 220 ms,队列现在 = [2, 3]
  空队列:poll() = null,poll(100 ms) = null
  remove() 抛 NoSuchElementException
  element() 抛 NoSuchElementException,peek() = null
  take() 在队列空时阻塞:消费者线程状态 = WAITING
  take() 阻塞时被 interrupt → InterruptedException

吞吐

实测 · 本机执行输出
  ArrayBlockingQueue(1000)   1 个生产者 + 1 个消费者:6.96 百万次/秒(后 3 轮取最好)
  ArrayBlockingQueue(1000)   4 个生产者 + 1 个消费者:16.62 百万次/秒(后 3 轮取最好)
  LinkedBlockingQueue(1000)  1 个生产者 + 1 个消费者:10.77 百万次/秒(后 3 轮取最好)
  LinkedBlockingQueue(1000)  4 个生产者 + 1 个消费者:11.60 百万次/秒(后 3 轮取最好)
  LinkedBlockingQueue(无界)    1 个生产者 + 1 个消费者:19.90 百万次/秒(后 3 轮取最好)
  LinkedBlockingQueue(无界)    4 个生产者 + 1 个消费者:11.63 百万次/秒(后 3 轮取最好)
  SynchronousQueue           1 个生产者 + 1 个消费者:0.41 百万次/秒(后 3 轮取最好)
  SynchronousQueue           4 个生产者 + 1 个消费者:0.34 百万次/秒(后 3 轮取最好)
  LinkedTransferQueue        1 个生产者 + 1 个消费者:25.00 百万次/秒(后 3 轮取最好)
  LinkedTransferQueue        4 个生产者 + 1 个消费者:8.80 百万次/秒(后 3 轮取最好)

常用模式

实测 · 本机执行输出
  消费者收到毒丸后正常退出,处理了 [a, b, c],队列剩余 0
  drainTo(list, 3):取走 3 个 [1, 2, 3],队列剩 [4, 5]
  PriorityBlockingQueue 依次 put 5 1 4 2 3,take 的顺序 = 1 2 3 4 5;无界,put 永远不阻塞
  SynchronousQueue:没有消费者时 offer("x") = false,size = 0,remainingCapacity = 0

分步演示

BlockingQueue
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12,10 核;吞吐是自写循环、每组 4 轮取后 3 轮最好
    • 没有验证:DelayQueue 与 LinkedTransferQueue 的 transfer 语义(见后面的篇目)、公平的 ArrayBlockingQueue、JMH 严格基准、虚拟线程下的表现、JDK 17 与 27 的数字
    京ICP备2026067502号-1京公网安备11010502063487号