SynchronousQueue在java中的元素增减

SynchronousQueue 是不存储元素的同步队列,其核心是实现生产者与消费者间的直接交接:put 需等待 take、take 需等待 put,offer/poll 非阻塞但立即返回,size 始终为 0,适用于零缓冲的即时协作场景。

SynchronousQueue 是 Java 并发包(java.util.concurrent)中一种特殊的阻塞队列,它**不存储元素**——也就是说,它没有内部容量,不能“增”也不能“减”传统意义上的队列元素。

为什么说它不支持常规的“增减”操作?

它的设计目标是实现**直接交接(hand-off)**:每个插入操作(put)必须等待另一个线程执行对应的移除操作(take),反之亦然。没有缓冲空间,没有暂存元素。

  • offer(e)put(e):不是“把元素加进去”,

    而是“尝试交给另一个正在等待取走它的线程”。如果此时没有匹配的消费者,offer 立即返回 falseput 则阻塞直到有线程调用 take
  • poll()take():不是“从队列里取出一个已有元素”,而是“尝试从某个正在等待交付的生产者那里接收一个元素”。如果没有匹配的生产者,poll 返回 nulltake 阻塞等待。
  • size() 始终返回 0isEmpty() 始终返回 true —— 因为它真的不保存任何东西。

常见使用场景:线程间“一来一回”的即时协作

它适合用于需要严格配对、零延迟传递的场景,比如:

  • 工作窃取线程池(如 ForkJoinPool 内部)中任务的快速交接
  • 生产者-消费者模型中要求“生产即消费”,不允许积压
  • 构建无缓冲的信号通道(例如:一个线程通知另一个线程“我准备好了”,对方立即响应)

简单示例:put/take 配对才能完成一次传递

下面这段代码不会卡死,因为两个线程在彼此等待时完成了元素交接:

SynchronousQueue sq = new SynchronousQueue<>();
new Thread(() -> {
    try { sq.put("hello"); } catch (InterruptedException e) {}
}).start();

new Thread(() -> {
    try { System.out.println(sq.take()); } catch (InterruptedException e) {}
}).start();
// 输出:hello

但如果只调用 put 而没有对应 take,线程会一直阻塞;同理,只调用 take 也会阻塞。

基本上就这些 —— 它不是用来“增减元素”的队列,而是用来“促成一次原子*接”的同步工具。