目录
📕 引言
🌲阻塞队列是什么
阻塞队列是一种特殊的队列. 也遵守 “先进先出” 的原则.
阻塞队列能是一种线程安全的数据结构, 并且具有以下特性:
当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素
当队列空的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插入元素
阻塞队列的一个典型应用场景就是 “生产者消费者模型”. 这是一种非常典型的开发模型
🌳生产者消费者模型
一种解决问题的典型方案(多线程问题).
生产者消费者模式有很多优势,其中两个最主要的是解耦合,削峰填谷
🚩 耦合
耦合是两个或多个模块之间的相互关联。在软件工程中,两个模块之间的耦合度越高,维护成本越高。因此,在系统架构的设计过程中,应减少各个模块之间的耦合度,以提高应用的可维护性
耦合又分为紧耦合(强耦合)和 松耦合(削峰填谷)
📌解耦合(松耦合)
🚩 削峰填谷
🎄Java标准库中的阻塞队列的使用
在Java标准库中内置了阻塞队列. 如果需要在一些程序中使用阻塞队列, 直接使用标准库中即可
使用注意事项:
BlockingQueue 是一个接口. 真正实现的类是有以下几种
咱们new 的时候,可选的选项为以下三个: (数组,链表,堆),实际用的时候哪个都可以
报错的原因:我们要指定一个容量
通过一些方法进行操作:
阻塞队列只需要考虑入队列和出队列即可,阻塞队列没有"取队首元素"操作(也不是没有,只不过没有阻塞功能)。
put 方法用于阻塞式的入队列, take 用于阻塞式的出队列
BlockingQueue 也有== offer, poll, peek 等方法, 但是这些方法不带有阻塞特性==
注意:put操作要进行异常处理
如果说入队只入了一个元素,出队的时候出两次,那么第二次打印的时候,此时的take就被阻塞到了,一直阻塞到有其他线程往这个队列里面插入元素为止
🚩 标准库实现消费者生产者模型
代码一:(生产慢,消费快)
虽然生产者是每隔1s生产一个元素,但是消费者里面会跟着生产者的节奏进行打印,消费者没有sleep,take自带的阻塞特性,也使消费者处于一个比较慢的速度进行消费。
代码二:(生产快,消费慢)
一瞬间就生产了1000个元素,此时队列就满了,要想在生产元素只能消费一个元素之后,才能生产一个元素......谁慢,另一个就将就谁
链表也能够起到阻塞,通过capacity来控制。
🍀阻塞队列的模拟实现
这里我们又两种结构可以选择,一种是链表的,另一种是数组的形式实现
这里选择的是用数组的形式进行模拟实现
首先我们是通过"循环队列",在数据结构部分已经实现过!
步骤:
- 写一个普通的队列
- 再加上线程安全
- 引入阻塞
普通队列:(入队,出队)
线程安全:(加锁以及针对变量的内存可见性和指令重排序)
引入阻塞:(队列满进行阻塞,队列空进行阻塞)
注意要确保加锁中的的对象和你调用wiat的对象,以及调用notify的对象是一致的。
此时呢代码还有点小小的问题:
在 wait 源码中注释说,wait 不仅仅会被 notify 和 notifyAll 唤醒,也可能会被其他线程唤醒,比如 interrupt。所以建议我们使用 wait 的时候搭配 while 来使用。
代码:(生产慢,消费快)
生产快,消费慢将sleep换到消费者即可,这里就不再演示了。