synchronousqueue 是一个不存储元素的阻塞队列,要求每个 put 必须等待对应 take,实现无缓冲、零拷贝、严格同步的线程间数据交换,核心是容量为 0 的配对原子操作。

SynchronousQueue 是一个不存储元素的阻塞队列,它要求每个插入操作(put)必须等待另一个线程执行对应的移除操作(take),反之亦然。这种“手递手”式的数据传递机制,天然适合实现两个线程间**无缓冲、零拷贝、严格同步**的数据交换。
核心原理:配对即完成
它内部没有容量(capacity = 0),不缓存任何数据。一个线程调用 put() 后会立即阻塞,直到另一个线程恰好调用 take();此时数据直接从生产者线程“移交”给消费者线程,不经过队列中转。整个过程是原子性的配对操作。
- 不是“放入队列 → 稍后取出”,而是“你放,我立刻接;我取,你正在给”
- 线程间传递的是对象引用,无额外内存复制或队列暂存
- 适用于严格一对一协作场景(如工作窃取、主从线程指令下发)
典型使用模式:生产者-消费者配对
需确保两个线程严格配合:一个只 put,一个只 take,且调用时机交错。常见写法如下:
// 初始化
SynchronousQueue<string> sq = new SynchronousQueue();
// 线程 A(生产者)
new Thread(() -> {
try {
System.out.println("A 准备发送 'hello'");
sq.put("hello"); // 阻塞,直到 B 调用 take
System.out.println("A 已交付");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 线程 B(消费者)
new Thread(() -> {
try {
System.out.println("B 准备接收");
String msg = sq.take(); // 阻塞,直到 A 调用 put
System.out.println("B 收到: " + msg);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
</string>
输出顺序严格为:
A 准备发送 'hello'
B 准备接收
B 收到: hello
A 已交付
说明交付是同步阻塞、逐次配对的。
注意事项与避坑点
-
不能重复使用单个实例做多轮通信而不协调:若 A 连续两次
put,第二次会永远阻塞(因 B 只take一次),需确保调用节奏匹配 -
不支持 null 元素:传入 null 会抛
NullPointerException -
推荐搭配线程池谨慎使用:在
Executors.newCachedThreadPool()中使用较自然(线程可复用且能及时响应配对),但避免在固定大小线程池中造成死锁风险 -
超时控制更安全:用
offer(e, timeout, unit)和poll(timeout, unit)替代无参方法,防止无限等待
适用场景举例
- 主线程向工作线程下发一条配置指令,并等待执行确认(如重启服务)
- 测试中模拟“请求-响应”强同步调用,绕过异步回调逻辑
- 实现简单的双人协作协议:A 计算出值 → B 拿到后立即处理 → A 继续下一步
- 替代 wait/notify 的轻量级线程握手(语义更清晰,无需 synchronized 块)










