SynchronousQueue的核心是直接交接,无缓存容量,通过Transferer接口统一抽象TransferStack(LIFO栈)和TransferQueue(FIFO队列)两种模式,put/take等操作均委托transfer方法实现配对传递。

SynchronousQueue 的核心在于“直接交接”——生产者线程必须等待消费者线程到来,才能完成元素传递,反之亦然。它不缓存元素,因此内部不维护传统意义上的队列容量,而是通过 Transferer 接口统一抽象两种协作模式:**栈式(LIFO)** 和 **队列式(FIFO)**。JDK 中这两个实现分别是 TransferStack 和 TransferQueue,它们都实现了 Transferer 接口的 transfer 方法。
Transferer 接口的核心契约
Transferer 只有一个关键方法:
含义是:尝试将元素 e 传给配对线程;若 e == null,表示当前线程是消费者,等待获取元素;否则是生产者,等待交付元素。方法返回值有三种情况:
- 非
null且非e→ 成功配对,返回对方传来的元素(即完成交换) - 返回
e自身 → 超时或中断,未配对成功,操作失败 - 返回
null→ 当前线程是消费者且e == null,但配对成功拿到了元素(注意:此时实际拿到的元素被封装在内部节点中,方法返回null是历史设计,由调用方从节点字段读取)
栈式实现(TransferStack):后进先出的等待链
栈模式下,等待线程按 LIFO 组织成一个链表(CAS 拼接的栈),新来的线程总是压到栈顶。配对只发生在栈顶两个相邻节点之间:
立即学习“Java免费学习笔记(深入)”;
- 若栈为空,当前线程入栈等待(生产者 push node,消费者也 push node)
- 若栈顶是“互补类型”(如栈顶是生产者节点,当前是消费者),则 CAS 弹出栈顶,完成数据交换,双方唤醒退出
- 若栈顶是“同类型”,则当前线程也入栈,形成连续同类型等待(如多个生产者叠在一起)
- 取消或超时时,节点标记为
CANCELLED,后续操作会帮助清理(help clean)
栈的优势是局部性好、竞争低,适合任务窃取等场景;缺点是可能饥饿(早来的消费者一直等不到晚来的生产者)。
队列式实现(TransferQueue):先进先出的等待环形队列
队列模式使用带 head/tail 指针的无锁单向队列(类似 ConcurrentLinkedQueue),但节点类型更丰富:
- 节点包含
item字段:生产者写入元素,消费者为null - 线程入队前先检查 tail:若 tail 是互补节点(如 tail 是生产者,当前是消费者),则尝试 CAS 匹配并推进指针
- 匹配失败则新建节点入队(CAS 更新 tail),然后自旋 + 阻塞等待被唤醒
- 被匹配后,对方线程会设置本节点的
match字段并唤醒,当前线程苏醒后读取match返回结果 - 支持“干净出队”:匹配完成后自动移除已配对节点对,维持队列紧凑
队列保证公平性(FIFO),避免饥饿,适合对响应顺序敏感的场景,但同步开销略高于栈。
如何选择栈还是队列?由构造函数决定
SynchronousQueue 构造时可通过布尔参数指定:
-
new SynchronousQueue()→ 默认使用TransferQueue(FIFO) -
new SynchronousQueue(true)→ 显式启用TransferStack(LIFO)
两者共享同一套 Transferer 抽象,上层 put/take/offer/poll 等方法都委托给 transfer,完全屏蔽底层差异。这种分离让算法可插拔,也体现了 Doug Lea 在并发容器设计中“接口抽象 + 多实现”的经典思路。


















