BlockingQueue与SynchronousQueue

BlockingQueue

BlockingQueue 是一个支持阻塞操作 的队列接口,位于 java.util.concurrent 包中。它的典型应用就是生产者-消费者模型。

需要首先给队列设置容量

BlockingQueue<String> queue = new ArrayBlockingQueue<>(3); // 容量=3

BlockingQueue 的四组 API

1. 抛异常

  • 当队列满时,再 add() 会抛异常;
  • 当队列空时,再 remove() 会抛异常。
scss 复制代码
add(e)       // 插入元素,成功返回 true,满时抛 IllegalStateException
remove()     // 移除队首元素,空时抛 NoSuchElementException
element()    // 查看队首元素,空时抛 NoSuchElementException

2. 返回特殊值

  • 满了插入失败返回 false;
  • 空了移除失败返回 null。
scss 复制代码
offer(e)     // 插入元素,成功返回 true,满时返回 false
poll()       // 移除队首元素,空时返回 null
peek()       // 查看队首元素,空时返回 null

3. 一直阻塞

  • 满了 put() 会一直阻塞,直到队列有空间;
  • 空了 take() 会一直阻塞,直到队列有元素。
scss 复制代码
put(e)       // 插入元素,满时阻塞
take()       // 移除并返回队首元素,空时阻塞

4.超时退出

  • 满了 offer(e, time, unit) 会等待指定时间,超时返回 false;
  • 空了 poll(time, unit) 会等待指定时间,超时返回 null。
scss 复制代码
offer(e, 2, TimeUnit.SECONDS)   // 等待 2 秒插入,失败返回 false
poll(2, TimeUnit.SECONDS)       // 等待 2 秒取出,失败返回 null
操作类型 抛异常 特殊值 阻塞 超时
插入 add(e) offer(e) put(e) offer(e, time, unit)
移除 remove() poll() take() poll(time, unit)
检查队首 element() peek() ------ ------

适合场景

  • 抛异常:适合严格要求,必须处理异常的场景。
  • 返回特殊值:适合可以容忍失败的场景。
  • 阻塞:适合生产者-消费者模型,不急着返回,等资源可用。
  • 超时:适合对实时性有要求的场景,不能无限等待。

SynchronousQueue

SynchronousQueue 是 Java 并发包里一个 非常特殊的 BlockingQueue 。它和其他 BlockingQueue(如 ArrayBlockingQueue、LinkedBlockingQueue)不一样,它的容量永远是 0,没有任何内部缓冲。

特点

  1. 容量为 0
    • 不能存放任何元素。
    • put() 一个元素时,必须有另一个线程正在 take(),否则会阻塞。
    • take() 时也必须有线程 put(),否则也会阻塞。
  1. 一手交钱一手交货
    • 类似一个"当面交易"的模型。
    • 生产者线程和消费者线程必须同时到场,才能完成元素的交接。
  1. 线程间直接传递数据
    • 没有缓存区,数据不存储,而是 直接在两个线程之间传递

API 行为

  • put(e):如果没有消费者在等待,阻塞。
  • take():如果没有生产者在等待,阻塞。
  • offer(e, timeout, unit):在超时时间内,如果有消费者等待就成功,否则返回 false。
  • poll(timeout, unit):在超时时间内如果有生产者等待就取到数据,否则返回 null。

使用场景

  1. 线程间直接交换消息
    • 两个线程需要强同步(例如生产者和消费者一对一交接)。
  1. 线程池的工作队列(Executors.newCachedThreadPool 默认使用它)
    • 因为 SynchronousQueue 不存储任务,提交的任务必须马上被某个线程取走执行,如果没有空闲线程,就创建新线程。
    • 这也是为什么 CachedThreadPool 会无限制创建线程的原因。
相关推荐
雾行0x002 小时前
粗排:如何在毫秒级筛出值得精排的内容?
后端·架构
D121382 小时前
从零用 C++17 写一个 API 网关:线程模型、控制面分离,以及三个真实 Bug
后端
Scene2162 小时前
AgentScope Java 进阶:2.如何精准定义问题类型与处理“不确定性”?
后端
用户345966815212 小时前
在嵌入式 Linux 摄像头上跑 WebRTC:metaRTC 8.0 的 8 个坑与解法
后端
今天早睡2 小时前
用 State、Reducer 和条件边搭出可控循环
后端
站大爷IP2 小时前
Python的字符串格式化把我坑惨了,原来f-string和format的区别这么大
后端
雾行0x002 小时前
精排:推荐系统如何给内容打分?从多目标预估到分数融合
后端·架构
今天早睡2 小时前
Checkpointer 和 thread_id 如何让状态跨调用延续
后端
用户423341246192 小时前
Redis 缓存穿透、击穿、雪崩,一次性讲清楚
后端
姜鱼问生2 小时前
2G 内存跑 5 个容器频频 OOM?一招 Swap 抢救你的 2核2G 小破服务器
后端