BlockingQueue详解

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BlockingQueue详解相关的知识,希望对你有一定的参考价值。

参考技术A

在Java中,BlockingQueue是一个接口,它的实现类有 ArrayBlockingQueue、DelayQueue、 LinkedBlockingDeque、 LinkedBlockingQueue、PriorityBlockingQueue、 SynchronousQueue等,它们的区别主要体现在存储结构上或对元素 操作上的不同,但是对于take与put操作的原理,却是类似的。

阻塞与非阻塞

LinkedBlockingQueue内部是使用链表实现一个队列的,但是有别于一般的队列,在于该队列至少是有一个节点的,头节点不含有元素。如果队列为空时,头节点的next参数为null。尾节点的next参数也为null。

1、LinkedBlockingQueue不允许插入的元素为null;

2、同一时刻只有一个线程可以进行入队操作,putLock在将元素插入队列尾部时加锁了;

3、如果队列满了,则会调用notFull.await(),将该线程加入到Condition等待队列中。await方法会释放线程占有的锁,这将导致之前由于被阻塞的入队线程将会获取到锁,执行到while循环处,不过可能因为队列仍旧是满的,也被进入到条件队列中;

4、一旦有出队线程取走元素,就会通知到入队等待队列释放线程。那么第一个加入到Condition队列中的将会被释放,那么该线程将会重新获得put锁,继而执行enqueue()方法,将节点插入到队列的尾部;

5、然后得到插入队列前元素的个数,如果插入后队列中还可以继续插入元素,那么就通知notFull条件的等待队列中的线程;

6、如果插入队列前个数为0,那现在插入后,就为1了,那就可以通知因为队列为空而导致阻塞的出队线程去取元素了。

1、同一时刻只有一个线程可以进行出队操作,takeLock在出队之前加锁了;

2、如果队列中元素为空,那就进入notEmpty队列中进行等待。直到队列不为空时,得到队列中的第一个元素。当发现取完发现还有元素可取时,再通知一下notEmpty队列中等待的其他线程。最后判断自己取元素前的是不是满的,如果是满的,那自己取完,就不满了,就可以通知在notFull队列中等待插入的线程进行put了。

LinkedBlockingQueue允许两个线程同时在两端进行入队和出队操作,但一端同时只能有一个线程进行操作,是通过两个锁进行区分的。

为了维护底部数据的统一,引入了AtomicInteger的一个count变量,表示队列中元素的个数。count只能在两个地方变化,一个是入队的方法(进行+1操作),另一个是出队的方法(进行-1操作),而AtomicInteger是原子安全的,所以也就确保了底层队列的数据同步。

ArrayBlockingQueue内部是使用数组实现一个队列的,并且在构造方法中就需要指定容量,也就意味着底层数组一旦创建了,容量就不能改变了,因此ArrayBlockingQueue是一个容量限制的阻塞队列。因此在队列满的时候执行入队会阻塞,在队列为空时出队也会阻塞。

1、ArrayBlockingQueue不允许添加null元素;

2、ArrayBlockingQueue在队列已满的时候,会调用notFull.await(),释放锁并处于阻塞状态;

3、一旦ArrayBlockingQueue在队列不满的时候,就立即入队。

1、取元素时,一旦获得锁,队列为空, 则会阻塞,直至不为空,调用dequeue()出队。

ArrayBlockingQueue是一个底层结构是数组的阻塞队列,是通过 ReentrantLock Condition 来实现的。不可插入为null的元素,入队和出队使用的是同一个锁。意味着同一时刻只能有一个线程能进行入队或者出队操作。入队时,队列已满则会调用notFull.await(),进入阻塞状态。直到队列不满时,再进行入队操作。当出队时,队列为空,则调用notEmpty.await(),进入阻塞状态,直到队列不为空时,则出队。

LinkedBlockingQueue底层实现是链表,ArrayBlockingQueue底层实现是数组

LinkedBlockingQueue默认的队列长度是Integer.Max,但是可以指定容量。在入队与出队都高并发的情况下,性能比ArrayBlockingQueue高很多;

ArrayBlockingQueue必须在构造方法中指定队列长度,不可变。在只有入队高并发或出队高并发的情况下,因为操作数组,且不需要扩容,性能很高。

LinkedBlockingQueue有两把锁,可以有两个线程同时进行入队和出队操作,但同时只能有一个线程进行入队或出队操作。

ArrayBlockingQueue只有一把锁,同时只能有一个线程进行入队和出队操作。

以上是关于BlockingQueue详解的主要内容,如果未能解决你的问题,请参考以下文章

BlockingQueue(阻塞队列)详解

BlockingQueue(阻塞队列)详解

BlockingQueue(阻塞队列)详解

BlockingQueue(阻塞队列)详解

线程池 execute() 的工作逻辑

多线程之阻塞队列BlockingQueue详解