SynchronousQueue 是一种不存储元素的同步点,核心语义是“直接移交”,生产者与消费者必须配对阻塞协作,容量恒为0,适用于强耦合、低延迟的线程间协作场景。

SynchronousQueue 不是用于“传递零容量数据”的容器,它根本不存储元素——它的核心语义是“直接移交”(hand-off),即生产者线程必须等待消费者线程就绪后才能完成插入操作,反之亦然。所谓“零容量”,指其内部队列长度恒为 0,任何 offer()、put() 都不会成功返回,除非同时有另一个线程正在执行 poll() 或 take()。
理解 SynchronousQueue 的协作机制
它本质上是一个同步点(synchronization point),不是缓冲区。每个插入操作都必须与一个移除操作配对发生,就像两个人面对面交接一个物品:交不出去就一直等着,接不到也一直等着。
- 调用
put(e)会阻塞,直到另一个线程调用take(); - 调用
take()会阻塞,直到另一个线程调用put(e); -
offer(e, timeout, unit)和poll(timeout, unit)支持超时,避免无限等待; -
isEmpty()始终返回true,size()永远是 0 —— 它没有“待处理数据”的概念。
典型使用场景:生产者-消费者强耦合协作
适合需要严格一对一、无缓冲、低延迟协调的场景,例如:
- 线程池中
newCachedThreadPool()的默认工作队列,任务提交和空闲线程唤醒直接绑定; - 两个线程之间传递控制信号或单次事件(如“准备就绪”、“开始执行”);
- 构建无锁管道(pipeline)中相邻阶段的精确同步点。
示例:主线程通知工作线程开始处理,并等待其完成确认:
立即学习“Java免费学习笔记(深入)”;
SynchronousQueue<String> queue = new SynchronousQueue<>();
Thread worker = new Thread(() -> {
try {
String cmd = queue.take(); // 等待指令
System.out.println("收到指令: " + cmd);
// 执行任务...
queue.put("DONE"); // 发送完成信号
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
worker.start();
queue.put("START"); // 主线程发出指令,阻塞直到 worker take
String result = queue.take(); // 阻塞等待 worker put DONE
System.out.println(result); // 输出 DONE
注意事项与常见误区
误把它当“轻量级无界队列”或“高性能空队列”会导致死锁或不可预期阻塞:
- 不能用于批量数据暂存,也不适合突发流量削峰;
- 若生产者未配对消费者,或消费者未配对生产者,线程将永久阻塞(除非设超时);
- 不支持
peek()、iterator()、toArray()等查看/遍历方法,所有访问都是双向阻塞式; - 公平模式(构造时传
true)按 FIFO 协调配对,非公平模式(默认)使用栈语义,可能造成饥饿,需根据协作逻辑谨慎选择。
替代方案对比
如果目标是“传递少量数据但允许短暂缓冲”,应考虑:
-
ArrayBlockingQueue(1):容量为 1 的有界队列,支持 peek 和非阻塞 offer/poll; -
Exchanger<T>:专为两个线程交换对象设计,语义更明确; -
Phaser或CountDownLatch:纯信号协调,不传递数据。
SynchronousQueue 的价值不在“容量为零”,而在于强制线程间**即时协作**——它不是数据容器,而是协作契约的运行时体现。



















