生产者与消费者模式,数组阻塞队列(ArrayBlockingQueue)

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生产者消费者模式最核心的部分是生产者与消费者之间的特殊容器,而阻塞队列是特殊容器最常见的实现。JDK中定义了阻塞队列接口BlockingQueue,JDK通过该接口为我们提供了很多种阻塞队列的实现,其中包括本节的主角ArrayBlockingQueue,该类位于java.util.concurrent.ArrayBlockingQueue.java。该类需要实现的核心方法如下,下面我们详细分析ArrayBlockingQueue的实现原理。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ba/baa203a1a053acf83b39a72cf6e4856f.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"从名字可以看出它的存储结构就是一个数组,即基于数组实现了一个FIFO的阻塞队列。新元素都插入到队列尾部,于是最先进入的元素在队列头而最后进入的元素在队列尾部。该数组是有界的,所以构造时需要制定数组的大小。此外,该阻塞队列还提供公平和非公平两种模式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/25/25b39b05d702217e44f299316d229cb2.webp","alt":"图片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"使用例子","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在分析 ArrayBlockingQueue的实现原理之前我们先通过几个例子来了解它的使用。例子一中我们创建了一个 ArrayBlockingQueue对象,然后通过五个线程去生产数据并put到阻塞队列,二主线程则充当消费端,不断将阻塞队列中的数据取出消费。程序输出如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7e/7e5b7de8da4594754f126c7307052341.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主要展示了阻塞队列空间满了产生阻塞,我们创建一个最大长度为4的阻塞队列,然后通过五个线程分别产生一个数据put到阻塞队列中,但由于阻塞队列最大长度为4,所以在put四个数据后会产生阻塞。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/60/60ca7959993f24e1f8df93d17ea59b57.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主要展示了阻塞队列为空时产生阻塞,我们创建一个最大长度为10的阻塞队列,然后没有生产者产生数据,却又一个消费者调用take消费数据。由于阻塞队列为空,所以产生了阻塞。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7c/7cf4fc04d9bdab2aeb3af72b6ac9817f.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"实现原理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们先看 ArrayBlockingQueue类的属性和构造函数。其中count变量表示队列的长度大小,takeIndex和putIndex分别表示队列入队和出队的索引,items是一个Object数组用于保存队列的元素,lock是并发控制锁,notEmpty和notFull分别表示队列非空时和非满时的条件。提供两个构造函数,我们在创建时指定队列的大小,另外一个是公平模式参数,默认是非公平的。锁对象使用的是ReentrantLock对象,公平机制也使用的是它的,它的公平机制我们在前面的章节中已经深入分析过了。notEmpty和notFull条件对象通过锁对象的newCondition创建。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2d/2da4d27889c83eb5856905549ca66640.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"put和take方法是阻塞队列的入队和出队方法,它们会间接调用enqueue和dequeue方法。其中put方法会检查入队的元素不能为null,需要先获取锁后才执行enqueue方法维护数组,如果数组长度已经达到最大长度则调用notFull条件的await方法等待。take方法会先获取锁后才执行dequeue方法维护数组,如果数组长度为0则调用notEmpty条件的await方法等待。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ae/ae327a1352a564a08b2898b930db9803.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"offer和poll方法则是支持超时的阻塞队列的入队和出队方法,可以看到主要的区别就在于它们分别调用了notFull和notEmpty条件的awaitNanos方法进行等待。入队时如果超过指定的超时时间则会返回false,表示入队超时导致失败。而出队时如果超过指定的超时时间则会返回null,表示出队超时导致失败。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a1/a19d83ead1fec772f416caf0c5698a04.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"总结 ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"围绕着JDK中BlockingQueue的其中一种实现,即ArrayBlockingQueue。它的基本结构是使用数组来保存阻塞队列的元素,数组的长度有限制,并且提供了公平和非公平模式。对它的实现原理进行分析,为了方便我们仅保留了ArrayBlockingQueue最核心的源码,只有我们能理解透彻这些内容就已经能掌握它的实现原理了。推荐阅读","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0f/0fccb26755ddfd8a6663e05533f0272d.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章