并发阻塞队列(BlockingQueue)— 生产者消费者模式核心部件
在分析阻塞队列之前我们先看生产者消费者模式,这是一个很常见的模式,生产者负责数据的生产,而消费者则负数据的消费。一般来说生产者与消费者的数量比例是m:n,该模式最大的好处就是将数据生产方与消费方进行了解耦,使得它们之间不会互相影响。为了将生产者和消费者连接起来,我们需要一个特殊的容器,该容器能存储生产者生产的数据,而消费者则能从该容器中取出数据。

我们可以通过厨师、桌子、顾客来说明生产者消费者模式,厨师就好比生产者,他们制作生产出来美食将放到桌子这个容器中,顾客则好比是消费者,他们从桌子上取出美食进行消费享用。
1|0阻塞队列
生产者消费者模式的核心部分就是生产者和消费者之间的那个特殊容器,我们通过实现一个线程安全且具有一定策略的容器便连接起两端的生产者和消费者。这个容器可以具有队列性质,也可以具有栈性质,亦或是其它数据结构。最常见的就是阻塞队列,队列保证了先进的数据先出,而阻塞则是队列已满时和队列为空时的处理策略,即队列已满时的入队操作和队列为空时的出队操作都会引起阻塞。
下图是阻塞队列工作示意图,线程一、线程二、线程三生产的数据通过put操作进行入队,线程四、线程五通过take操作进行出队,当队列满时put操作会阻塞等待消费者将队列的元素拿走,当队列为空时take操作会阻塞等待生产者将数据入队。

2|0模拟实现
根据前面对阻塞队列的介绍,我们试着来模拟实现一个简单的阻塞队列。先看数据结构的设计,可以使用一个数组来存放队列的元素,并通过head和tail指针来约束先进先出规则。入队操作使用tail指向的位置,而出队则使用head指向的位置,一旦到达数组尾部就重新从头开始。

下面看看具体的实现,Object数组用于保存元素,size表示队列的大小,此外还有head和tail指针。通过构造函数来指定阻塞队列大小,生产者生产的数据调put方法进行入队,如果size等于队列最大长度时则调用wait阻塞(此时队列已经满了),否则将元素保存到队列中,同时维护size和tail,最后如果size等于1时要调用notifyAll方法通知消费者可以消费了。消费者通过调用take方法进行数据消费,如果size等于0时则调用wait阻塞(队列为空需要等待),否则通过head获取队列头的元素,同时维护size和head,最后如果size等于queue.length-1时调用notifyAll方法通知生产者可以生产了。

3|0阻塞队列模拟实现
可以通过下面这个例子看模拟阻塞队列例子,输出结果如下,一个线程进行数据生产,一个线程进行数据消费。


4|0BlockingQueue 接口
接下去我们看JDK 的阻塞队列接口BlockingQueue,该接口早在 Java 5 时就已经引入了。JDK为我们提供了很多种阻塞队列的实现,比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue以及SynchronousQueue等等,它们都实现了BlockingQueue接口。该接口主要的核心方法如下,这些方法都支持中断,其中put方法和take方法我们都很熟悉的了,另外offer和poll两个方法其实对应的是put和take两个方法,只是它们提供了阻塞超时机制。
5|0总结
本节讲解了生产者消费者模式,该模式的核心部件是一种特殊的容器,而阻塞队列则是异常很常见的容器。然后我们通过一个数组和head、tail指针来模拟实现一个阻塞队列,其中put和take操作使用了synchronized控制多线程的访问,阻塞机制也由它提供。最后我们简单介绍了JDK的BlockingQueue接口,后面再分几个章节来详细分析ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue以及SynchronousQueue等阻塞队列的实现。

__EOF__
作 者:码农架构
出 处:https://www.cnblogs.com/ibytecoding/p/14277496.html
关于博主:目前任职蚂蚁金服,「公众号:码农架构」专注于系统架构、高可用、高性能、高并发类技术分享!
版权声明:署名 - 非商业性使用 - 禁止演绎,协议普通文本 | 协议法律文本。
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!