bigywater.tech
Java · 386

LinkedTransferQueue:生产者可以等待消费者

LinkedTransferQueue 比普通无界队列多了“生产者可以等消费者”。这篇量出它的方法语义、transfer 到底在等什么、做背压的效果,以及无界队列容易被忽略的 size() 成本和吞吐的不确定性。

17,978,953 vs 1
慢消费者下 put 与 transfer 两种用法 1 秒后队列里的积压元素数
1316 µs
100 万个元素时 LinkedTransferQueue.size() 的耗时(LinkedBlockingQueue 约 0 ns)
43 vs 179 ns
LinkedTransferQueue 在 1 对与 4 对生产者 / 消费者下每个对象的成本

transfer 的语义

实测 · 本机执行输出
  tryTransfer("a"):false;之后 size() = 0(没有消费者,元素没有入队)
  tryTransfer("b", 200 ms):false,耗时约 208 ms;之后 size() = 0(超时后元素被移除)
  put("c") 立即返回;size() = 1,hasWaitingConsumer() = false
  poll() = c
  transfer("d") 阻塞 300 ms 期间:size() = 1,peek() = d,线程状态 WAITING
  take() = d
  生产者 transfer("d") 在 323 ms 后返回
  一个消费者在 take() 等待时:hasWaitingConsumer() = true,getWaitingConsumerCount() = 1;tryTransfer("e") = true

transfer 等什么

实测 · 本机执行输出
  队列里 [1, 2, 3],另一个线程 transfer(99) 已等待 100 ms:size() = 4(99 在队尾,[1, 2, 3, 99])
  1 个元素被取走后(+100 ms):取到 1,transfer 线程状态 WAITING
  2 个元素被取走后(+200 ms):取到 2,transfer 线程状态 WAITING
  3 个元素被取走后(+300 ms):取到 3,transfer 线程状态 WAITING
  取走 99 之前 transfer 线程状态 WAITING;取走 99:99
  transfer(99) 总共等了约 555 ms(= 排在前面的 3 个元素被取走 + 99 本身被取走)

背压

实测 · 本机执行输出
  LinkedTransferQueue.put        1 秒内生产 14335204,消费 183,队列里积压 17978953
  LinkedTransferQueue.transfer   1 秒内生产 537,消费 537,队列里积压 1
  ArrayBlockingQueue(100).put    1 秒内生产 772,消费 672,队列里积压 100

size() 成本

实测 · 本机执行输出
  1000 个元素:LinkedTransferQueue.size() 最快一次约 15 µs,LinkedBlockingQueue.size() 约 84 ns
  100000 个元素:LinkedTransferQueue.size() 最快一次约 191 µs,LinkedBlockingQueue.size() 约 42 ns
  1000000 个元素:LinkedTransferQueue.size() 最快一次约 1316 µs,LinkedBlockingQueue.size() 约 0 ns
  LinkedTransferQueue.remainingCapacity() = 2147483647(没有容量上限)

吞吐

实测 · 本机执行输出
  1 对 LinkedTransferQueue          每个对象约 43 ns(中位数,共 170 ms)
  1 对 LinkedBlockingQueue          每个对象约 138 ns(中位数,共 551 ms)
  1 对 ConcurrentLinkedQueue(自旋)    每个对象约 38 ns(中位数,共 152 ms)
  4 对 LinkedTransferQueue          每个对象约 179 ns(中位数,共 714 ms)
  4 对 LinkedBlockingQueue          每个对象约 73 ns(中位数,共 292 ms)
  4 对 ConcurrentLinkedQueue(自旋)    每个对象约 163 ns(中位数,共 650 ms)

分步演示

LinkedTransferQueue
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12;吞吐数字是 5 轮中位数的 nanoTime 测量,不是 JMH,线程数 / 机器负载不同结果会变;10 个逻辑 CPU
    • 没有验证:getWaitingConsumerCount 在多消费者下的精确语义、超时 tryTransfer 的中断行为、内存占用(只看了积压元素数,没有量堆的大小)、其它 JDK 版本的实现差异
    京ICP备2026067502号-1京公网安备11010502063487号