其他分享
首页 > 其他分享> > 并发阻塞队列(BlockingQueue)— 生产者消费者模式核心部件

并发阻塞队列(BlockingQueue)— 生产者消费者模式核心部件

作者:互联网

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

 

我们可以通过厨师、桌子、顾客来说明生产者消费者模式,厨师就好比生产者,他们制作生产出来美食将放到桌子这个容器中,顾客则好比是消费者,他们从桌子上取出美食进行消费享用。

阻塞队列

生产者消费者模式的核心部分就是生产者和消费者之间的那个特殊容器,我们通过实现一个线程安全且具有一定策略的容器便连接起两端的生产者和消费者。这个容器可以具有队列性质,也可以具有栈性质,亦或是其它数据结构。最常见的就是阻塞队列,队列保证了先进的数据先出,而阻塞则是队列已满时和队列为空时的处理策略,即队列已满时的入队操作和队列为空时的出队操作都会引起阻塞。

 

下图是阻塞队列工作示意图,线程一、线程二、线程三生产的数据通过put操作进行入队,线程四、线程五通过take操作进行出队,当队列满时put操作会阻塞等待消费者将队列的元素拿走,当队列为空时take操作会阻塞等待生产者将数据入队。

 

模拟实现

根据前面对阻塞队列的介绍,我们试着来模拟实现一个简单的阻塞队列。先看数据结构的设计,可以使用一个数组来存放队列的元素,并通过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方法通知生产者可以生产了。

阻塞队列模拟实现

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

 

BlockingQueue 接口

接下去我们看JDK  的阻塞队列接口BlockingQueue,该接口早在 Java 5 时就已经引入了。JDK为我们提供了很多种阻塞队列的实现,比如ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue以及SynchronousQueue等等,它们都实现了BlockingQueue接口。该接口主要的核心方法如下,这些方法都支持中断,其中put方法和take方法我们都很熟悉的了,另外offer和poll两个方法其实对应的是put和take两个方法,只是它们提供了阻塞超时机制。

总结 

本节讲解了生产者消费者模式,该模式的核心部件是一种特殊的容器,而阻塞队列则是异常很常见的容器。然后我们通过一个数组和head、tail指针来模拟实现一个阻塞队列,其中put和take操作使用了synchronized控制多线程的访问,阻塞机制也由它提供。最后我们简单介绍了JDK的BlockingQueue接口,后面再分几个章节来详细分析ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue以及SynchronousQueue等阻塞队列的实现。

标签:消费者,队列,核心部件,阻塞,生产者,线程,size,BlockingQueue
来源: https://www.cnblogs.com/ibytecoding/p/14277496.html