商城首页欢迎来到中国正版软件门户

您的位置:首页 >SynchronousQueue是什么?无缓冲队列详解

SynchronousQueue是什么?无缓冲队列详解

  发布于2026-02-22 阅读(0)

扫一扫,手机访问

SynchronousQueue 的 size() 总是 0,因为它不存储元素,仅作为线程间直接交接的同步点,put/take 必须配对阻塞等待,无缓冲区,故 size() 恒为 0、isEmpty() 恒为 true,peek() 和 iterator() 均抛 UnsupportedOperationException。

什么是SynchronousQueue_不存储元素的阻塞队列及其在线程池的应用

为什么 SynchronousQueue 的 size() 总是 0?

因为 SynchronousQueue 根本不存数据——它不是“队列”,而是“交接点”。你调用 put(),线程就卡住,直到另一个线程正好在调 take();反过来也一样。中间没有缓冲区,没有数组,没有链表节点存着你的元素。所以 size() 永远返回 0isEmpty() 永远是 true,连 peek()iterator() 都直接抛 UnsupportedOperationException

  • 别用 offer(e, timeout, unit) 期望“尽力插入”——它和 put(e) 行为一致:超时前必须等到消费者,否则返回 false
  • 别把它当普通队列去遍历、检查、预判容量——所有这类操作都无效或抛异常
  • 调试时看到 size == 0 别慌,这是正常态,不是 bug

newCachedThreadPool 为什么非它不可?

Executors.newCachedThreadPool() 内部用的就是 SynchronousQueue,核心就一条:任务来了,有空闲线程就立刻交过去;没有,就新建线程。它不希望任务排队等——等就意味着延迟、积压、内存占用。

  • 如果换成 LinkedBlockingQueue(哪怕 capacity=1),任务就会先入队,再等线程来取,破坏“直交”语义
  • 一旦线程池最大线程数设得过高 + 队列有缓冲,突发流量可能撑爆堆内存;而 SynchronousQueue 强制把背压转成拒绝策略(如 AbortPolicy),更可控
  • 注意:它的非公平默认模式(TransferStack)可能导致新任务优先被最新创建的空闲线程抢走,而不是等待最久的线程——对低延迟敏感场景反而更优

自己 new SynchronousQueue 时 fair 参数怎么选?

构造时传 true 就是公平模式(用 TransferQueue),false 或无参就是非公平(TransferStack)。这不是“好不好”的问题,而是“要不要保序”。

  • 公平模式:按等待顺序匹配,先 put 的等得久,优先被后续 take 接走;适合需要确定性调度的场景,比如信号量式同步、严格 FIFO 的事件分发
  • 非公平模式:后到的 put 可能立刻匹配上前一个刚 take 完正在自旋的线程,吞吐略高,但顺序不可预测;newCachedThreadPool 默认用它,因为任务本身无序,快比稳重要
  • 公平 ≠ 更安全,也不等于更“正确”;只是多一次 CAS 比较 + 队列维护开销,压测下吞吐可能低 5–10%

常见误用:当成带缓冲的队列来用

最典型的错误是写这种逻辑:queue.offer(task) || fallbackToDisk(),以为 offer() 失败才走备选——但 SynchronousQueue.offer() 只有在没线程等着 take() 时才返回 false,且不阻塞。这跟 put() 的“必须等到”完全不同,极易引发逻辑错乱。

  • 想“有空就交,没空就丢/缓存”,应该用 offer(e, 0, TimeUnit.NANOSECONDS),而非无参 offer()
  • 千万别在单线程里先 put()take()——必然死锁,因为没人配合交接
  • 监控时发现大量线程 BLOCKED 在 put()take(),第一反应不该是扩容,而是查消费者线程是否挂了、阻塞了、或根本没启动
SynchronousQueue 的难,不在用法多复杂,而在它彻底反直觉:它要求你承认“传递必须双边在线”,任何单边假设都会出问题。写的时候多问一句“此刻对面真有人等着接吗”,比看文档还管用。
本文转载于:互联网 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注