synchronousqueue 是容量为0的同步移交队列,生产者put()与消费者take()必须即时配对,元素不经过存储直接线程间传递,适用于命令响应、事件驱动等强时序零缓冲场景。

SynchronousQueue 不是传统意义上的队列,它不存储元素,而是作为线程间“手递手”传递任务或数据的通道——生产者必须等待消费者就绪才能交付,消费者也必须等待生产者出现才能获取。这种即时配对、零缓冲的特性,天然适合需要变量实时传递、强时序控制、无中间状态残留的场景,比如命令触发、事件响应、协程式流程编排等。
理解 SynchronousQueue 的核心行为
它内部没有容量(capacity = 0),每次 put() 都会阻塞,直到另一个线程调用 take();反之亦然。这不是“缓存后通知”,而是直接线程交接:
- 调用
put(item)的线程会挂起,直到有其他线程正在执行take() - 一旦配对成功,item 不经过任何队列中转,直接从生产者栈帧传入消费者栈帧
- 不存在“多个任务堆积”或“旧值覆盖”问题——每次只能传递一个,且必须被立刻消费
实战:用 SynchronousQueue 实现命令-响应式变量传递
假设有一个主控线程下发参数(如 targetId、timeoutMs),工作线程需立即接收并执行,且不允许延迟或积压:
SynchronousQueue<map object>> commandQueue = new SynchronousQueue();
// 主控线程(发令)
new Thread(() -> {
Map<string object> cmd = new HashMap();
cmd.put("targetId", "user_123");
cmd.put("timeoutMs", 5000L);
try {
commandQueue.put(cmd); // 阻塞,直到 worker 调用 take()
System.out.println("指令已送达");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 工作线程(响应)
new Thread(() -> {
try {
Map<string object> cmd = commandQueue.take(); // 阻塞,直到主控调用 put()
System.out.println("收到指令:" + cmd);
// 立即处理,变量值就是此刻最新、唯一、未被篡改的快照
processCommand(cmd);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
</string></string></map>
关键细节与避坑指南
实际使用中需注意以下几点,否则容易陷入死锁或逻辑错位:
-
必须成对出现:每个
put()都要有对应take(),否则一方永久阻塞。建议配合超时操作(offer(item, timeout, unit)/poll(timeout, unit))防止卡死 -
不适合批量或异步解耦:若想“发完就走”,SynchronousQueue 不适用——应换用
LinkedBlockingQueue或ConcurrentLinkedQueue -
变量需线程安全封装:虽然传递是原子的,但若 Map 中的 value 是可变对象(如 ArrayList),仍需确保消费者不与生产者并发修改同一实例;推荐传递不可变容器(
Map.copyOf())或只读视图 -
支持公平策略:构造时传
true可启用 FIFO 公平模式(默认非公平),避免某些线程长期饥饿,在严格顺序场景下更可控
延伸:模拟轻量级协程通信
结合 ThreadLocal 与 SynchronousQueue,可在两个协作线程间建立单向“请求-应答”通道:
- 主线程通过 queue 发送请求,同时用
ThreadLocal<synchronousqueue>></synchronousqueue>绑定专属响应通道 - 工作线程处理完后,将结果
put到该线程局部的响应队列 - 主线程在发送后立即
take()自己的响应队列,实现“发起即等待结果”的同步语义,但底层仍是两线程协作
这种方式规避了 Future 的调度开销和回调嵌套,又比 wait/notify 更简洁明确,适合低延迟、高确定性的本地协同任务。










