synchronousqueue 是一种不存储元素的阻塞队列,实现线程间直接交付:put() 和 take() 必须配对执行,无缓冲、无容量概念,仅用于一对一即时协作。

SynchronousQueue 是 Java 并发包中一种特殊的阻塞队列,它**不存储元素**,每个插入操作必须等待另一个线程的移除操作,反之亦然。它实现的是“**直接交付(hand-off)**”语义——生产者线程不会把任务放入队列,而是**直接把任务交给消费者线程**,中间不经过任何缓冲。这种机制天然适合用于线程间“一对一”协作场景,比如工作窃取、任务委派、资源池交接等。
为什么 SynchronousQueue 没有缓冲?
它的内部没有数组或链表结构来暂存数据。当你调用 put(),当前线程会**立即阻塞**,直到有其他线程恰好在执行 take();同理,take() 也会阻塞,直到有线程调用 put()。这种“配对即完成”的行为,本质上是通过 AQS 的双向阻塞队列 + Transferer(如 TransferStack 或 TransferQueue)实现的,底层依赖 CAS 和 LockSupport 配合完成线程唤醒。
所以它不是“容量为 0 的队列”,而是“**容量概念本身不适用**”——它不保留任何待处理任务,只促成两个线程的即时握手。
典型使用方式:生产者-消费者配对
下面是一个最小可运行示例,展示两个线程如何通过 SynchronousQueue 完成一次无缓冲交付:
var queue = new SynchronousQueue<string>();
// 生产者线程:put 后立即阻塞,直到消费者 take
new Thread(() -> {
try {
System.out.println("Producer: putting 'hello'");
queue.put("hello"); // 阻塞在此,直到被消费
System.out.println("Producer: delivered");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者线程:take 后立即阻塞,直到生产者 put
new Thread(() -> {
try {
System.out.println("Consumer: waiting to take");
String msg = queue.take(); // 阻塞在此,直到有数据
System.out.println("Consumer: got '" + msg + "'");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
</string>
输出顺序固定(因阻塞同步):
Producer: putting 'hello' Consumer: waiting to take Consumer: got 'hello' Producer: delivered
注意:如果先启动消费者再启动生产者,效果一样;但若只启动一方,它将永远阻塞——这正是“无缓冲+强配对”的体现。
配合线程池实现“无队列”执行策略
最常见的实战场景是与 ThreadPoolExecutor 结合,用 SynchronousQueue 替代传统有界/无界队列,实现“来了就交、不排队、不缓存”的调度模型:
- 当核心线程都在忙,新任务提交时,
execute()会尝试向SynchronousQueue插入;插入失败(无人消费)则触发RejectedExecutionHandler - 只要有一个空闲线程,它就会立刻从队列中
take()并执行,完全跳过排队环节 - 因此该组合常用于
Executors.newCachedThreadPool()的底层实现(其队列就是SynchronousQueue)
手动构造示例:
var executor = new ThreadPoolExecutor(
0, // corePoolSize:允许创建 0 个常驻线程
Integer.MAX_VALUE,// maxPoolSize:按需创建
60L, TimeUnit.SECONDS,
new SynchronousQueue() // 关键:无缓冲交付
);
executor.execute(() -> System.out.println("Task running on " + Thread.currentThread().getName()));
这种配置下,每个任务都由一个新线程(或刚空闲下来的线程)**直接接收并执行**,没有任何中间队列滞留。
注意事项和常见误区
-
不能用 offer()/poll() 做非阻塞试探:虽然这些方法存在,但它们在 SynchronousQueue 中总是立即返回
false(offer)或null(poll),因为“无缓冲”意味着无法异步存放——要么配对成功,要么失败,没有中间态 - 慎用于高并发写入且消费者响应慢的场景:若消费者处理缓慢,生产者会大量阻塞,可能拖垮整个调用链;此时应考虑是否真的需要严格同步交付,还是改用带限流能力的有界队列
-
不支持迭代器、size()、clear() 等集合常规操作:调用
size()总返回 0,iterator()返回空迭代器,clear()无实际效果——再次印证它不是一个“容器”,而是一个“通道”
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











