生产者消费者怎么阻塞、怎么唤醒?不同队列有什么区别?这篇把每个 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