阻塞队列--概述

Posted

tags:

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

参考技术A 首先通过接口类BlockingQueue中的注释来简单了解阻塞队列。
阻塞队列是一个支持附加操作的特殊队列:在队列为空时回收元素会阻塞等待直到队列非空,或在队列已满时插入元素,会阻塞等待直到队列不满。

阻塞队列的方法提供了四种不同的处理方式:抛异常、返回特殊值(null或false)、阻塞当前线程直到操作成功以及阻塞一段时间,超时退出。这四种处理方式分别对应不同的函数接口:

是一个用数组实现的有界阻塞队列,按先进先出的原则对元素进行排序。put和take方法分别为添加和删除的阻塞方法。默认情况下不保证线程公平。
ArrayBlockingQueue内部使用一把重入锁ReentrantLock来保证多个线程之间的插入删除元素的同步;同时使用两个条件对象Condition来实现阻塞逻辑,调用其await和signal方法来实现线程的等待和唤醒。
由于使用了ReentrantLock,所以ArrayBlockingQueue存在线程公平与不公平两种选择。
插入删除元素的具体执行逻辑: ArrayBlockingQueue
PS:这7个阻塞队列本来想着一个一个解析的,但看了下源码,逻辑其实没有很复杂,所以后面几个就只记一下内部实现的主要点。

用链表实现的有界阻塞队列,默认和最大长度为Integer.MAX_VALUE,队列按照先进先出的原则对元素排序。
插入元素在表尾,删除元素在表头。
LinkedBlockingQueue内部使用了两把重入锁ReentrantLock,分别用来保护插入操作和删除操作。
同样也是使用两个条件对象来实现阻塞逻辑。

支持优先级的无界阻塞队列,默认按元素自然顺序升序排列,可通过自定义类实现compareTo方法或指定构造参数Comparator来指定元素排序规则,不保证同优先级元素的顺序。
PriorityBlockingQueue内部使用的是数组对象来存储元素,且数组容量初始化为11。
其内部只使用了一把重入锁ReentrantLock,和一个条件对象Condition,只用于阻塞和唤醒删除元素操作的线程。
当插入元素时,若此时数组已满,也不需要等待,它会尝试扩容,因此插入操作也不会有阻塞的可能。
PriorityBlockingQueue内部还有一个allocationSpinLock自旋锁,用于扩容时的同步保护,在执行扩容操作前,需先自旋尝试将allocationSpinLock置为1,设置成功后才能继续往下执行。

一个不存储元素的阻塞队列,每一个put的线程会阻塞到直到有一个take线程取走元素为止,每一个take的线程会阻塞到直到有一个put的线程放入元素为止。
由于SynchronousQueue不存储元素,所以类似peek操作或者迭代器操作都是无效的。
支持公平访问队列,默认情况下线程采用非公平性策略访问队列。
SynchronousQueue只是一个对外的封装层,其真正的实现逻辑在其类型为Transferer的成员变量transferer的transfer方法中;抽象类Transferer有两个具体的实现类:TransferStack和TransferQueue,分别在非公平和公平的模式下使用。
Transferer类内部是通过自旋锁及CAS操作实现多个线程间的同步。
SynchronousQueue可当做一个传递中介,负责将生产者线程处理的数据直接传递给消费者线程,适用于传递性场景,吞吐量高。
其内部的具体实现逻辑可参考: SynchronousQueue

LinkedTransferQueue内部是通过自旋以及CAS操作来实现线程间的同步。

有链表结构组成的双向阻塞队列,即可以从队列的两端插入或移除元素。
其内部同样拥有一把重入锁ReentrantLock,两个条件对象notEmpty和notFull。整体逻辑同LinkedBlockingQueue相似。
在初始化LinkedBlockingDeque时可以设置容量防止其过度膨胀,双向阻塞队列可以运用在“工作窃取”模式中。

以上是关于阻塞队列--概述的主要内容,如果未能解决你的问题,请参考以下文章

10_阻塞队列

Java多线程:阻塞队列与等待唤醒机制初探

Java并发多线程编程——阻塞队列(BlockingQueue)

java阻塞队列小结

并发编程-J.U.C组件拓展之阻塞队列BlockingQueue

一文详述DMS资源池队列阻塞告警及原理