首先分享之前的所有文章 , 欢迎点赞收藏转发三连下次一定 >>>> 😜😜😜
文章合集 : 🎁 juejin.cn/post/694164…
Github : 👉 github.com/black-ant
CASE 备份 : 👉 gitee.com/antblack/ca…
如题 , 阻塞队列真没啥好说的 , 工具类 , 了解功能感觉就可以了
一 . 阻塞队列简述
阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是:
- 在队列为空时,获取元素的线程会等待队列变为非空。
- 当队列满时,存储元素的线程会等待队列可用。
阻塞队列常用于生产者和消费者的场景:
- 生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程
- 阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
通用概念
- implements Queue : 基本上都是Queue的实现类 ,即实现了 Queue 的方法
- 可以通过构造方法初始化容量和排序
- 构造方法可以传入整个集合
队列类型
- ArrayBlockingQueue :一个由 数组 结构组成的 有界 阻塞队列。
- LinkedBlockingQueue :一个由 链表 结构组成的 无界 阻塞队列。
- PriorityBlockingQueue :一个 支持优先级排序 的 无界 阻塞队列。
- DelayQueue:一个使用 优先级队列 实现的 无界 阻塞队列。
- SynchronousQueue:一个 不存储元素 的阻塞队列。
- LinkedTransferQueue:一个由 链表 结构组成的 无界 阻塞队列。
- LinkedBlockingDeque:一个由 链表 结构组成的双向阻塞队列。
1 | java复制代码 |
性能对比
- 1、ArrayBlockingQueue 性能优于LinkedBlockingQueue,但是LinkedBlockingQueue是无界的。
- 2、ArrayBlockingQueue 和 LinkedBlockingQueue 的 poll方法总是比offer方法快,并发越高,差距越大
- 3、ArrayBlockingQueue 和 LinkedBlockingQueue 的 性能远高于PriorityBlockingQueue,显然优先队列在比较优先级上的操作上耗费太多
- 4、PriorityBlockingQueue的 offer方法与 poll方法的性能差距很小,基本维持在近似1:1
线程数 | 20 | 50 | 100 | 200 | 500 | 1000 |
---|---|---|---|---|---|---|
LinkedBlockingQueue | 15,0 | 31,15 | 32,16 | 63,32 | 203,47 | 563,110 |
ArrayBlockingQueue | 15,0 | 16,15 | 31,15 | 47,16 | 125,47 | 364,68 |
PriorityBlockingQueue | 78,78 | 172,188 | 360,422 | 813,969 | 3094,2641 | 6547,5453 |
二. ArrayBlockingQueue
1 | JAVA复制代码> 一个由数组实现的有界阻塞队列。该队列采用 FIFO 的原则对元素进行排序添加的 |
三 . DelayQueue
1 | java复制代码支持延时获取元素的无界阻塞队列。里面的元素全部都是“可延期”的元素,列头的元素是最先“到期”的元素 |
四 . SynchronousQueue
- SynchronousQueue没有容量。
- 与其他BlockingQueue不同,SynchronousQueue是一个不存储元素的BlockingQueue。每一个put操作必须要等待一个take操作,否则不能继续添加元素,反之亦然。
- 因为没有容量,所以对应 peek, contains, clear, isEmpty … 等方法其实是无效的。
- 例如clear是不执行任何操作的,contains始终返回false,peek始终返回null。
- SynchronousQueue分为公平和非公平,默认情况下采用非公平性访问策略,当然也可以通过构造函数来设置为公平性访问策略(为true即可)。
- 若使用 TransferQueue, 则队列中永远会存在一个 dummy node(这点后面详细阐述)。
SynchronousQueue非常适合做交换工作,生产者的线程和消费者的线程同步以传递某些信息、事件或者任务。
1 | java复制代码C- SynchronousQueue |
五 . LinkedBlockingDeque
- 一个有链表组成的双向阻塞队列,与前面的阻塞队列相比它支持从两端插入和移出元素。
- 以first结尾的表示从对头操作,以last结尾的表示从对尾操作。
- 支持FIFO、FILO两种操作方式
LinkedBlockingQueue是一个阻塞队列
- 内部由两个ReentrantLock来实现出入队列的线程安全,由各自的Condition对象的await和signal来实现等待和唤醒功能。
- 基于单向链表的、范围任意的(其实是有界的)、FIFO 阻塞队列。
- 头结点和尾结点一开始总是指向一个哨兵的结点,它不持有实际数据
- 当队列中有数据时,头结点仍然指向这个哨兵,尾结点指向有效数据的最后一个结点。
- 这样做的好处在于,与计数器 count 结合后,对队头、队尾的访问可以独立进行,而不需要判断头结点与尾结点的关系。
1 | java复制代码 |
六 . LinkedTransferQueue
- LinkedTransferQueue是一个由链表组成的的无界阻塞队列
- 它是ConcurrentLinkedQueue、SynchronousQueue (公平模式下)、无界的LinkedBlockingQueues等的超集。
与其他BlockingQueue相比,他多实现了一个接口TransferQueue, 该接口是对BlockingQueue的一种补充,多了tryTranfer()和transfer()两类方法:
- tranfer():若当前存在一个正在等待获取的消费者线程,即立刻移交之。
- 否则,会插入当前元素e到队列尾部,并且等待进入阻塞状态,到有消费者线程取走该元素
- tryTranfer(): 若当前存在一个正在等待获取的消费者线程(使用take()或者poll()函数),使用该方法会即刻转移/传输对象元素e;
- 若不存在,则返回false,并且不进入队列。这是一个不阻塞的操作
七 . PriorityBlockingQueue
1 | java复制代码- PriorityBlockingQueue是支持优先级的无界队列。 |
八 . ArrayBlockingQueue 与 LinkedBlockingQueue 的区别
Queue | 阻塞与否 | 是否有界 | 线程安全保障 | 适用场景 | 注意事项 |
---|---|---|---|---|---|
ArrayBlockingQueue | 阻塞 | 有界 | 一把全局锁 | 生产消费模型,平衡两边处理速度 | 用于存储队列元素的存储空间是预先分配的,使用过程中内存开销较小(无须动态申请存储空间) |
LinkedBlockingQueue | 阻塞 | 可配置 | 存取采用 2 把锁 | 生产消费模型,平衡两边处理速度 | 无界的时候注意内存溢出问题,用于存储队列元素的存储空间是在其使用过程中动态分配的,因此它可能会增加 JVM 垃圾回收的负担。 |
九 . 双端队列
而 ArrayDeque、LinkedBlockingDeque 就是双端队列,类名以 Deque 结尾
正如阻塞队列适用于生产者消费者模式,双端队列同样适用与另一种模式,即工作密取。
- 在生产者-消费者设计中,所有消费者共享一个工作队列,而在工作密取中,每个消费者都有各自的双端队列。
- 如果一个消费者完成了自己双端队列中的全部工作,那么他就可以从其他消费者的双端队列末尾秘密的获取工作。
- 具有更好的可伸缩性,这是因为工作者线程不会在单个共享的任务队列上发生竞争。
- 在大多数时候,他们都只是访问自己的双端队列,从而极大的减少了竞争。
- 当工作者线程需要访问另一个队列时,它会从队列的尾部而不是头部获取工作,因此进一步降低了队列上的竞争。
十 . 队列对象
1 | java复制代码> 阻塞队列 : 阻塞队列有普通的先进先出队列, |
十一 . CopyOnWriteArrayList
CopyOnWrite容器即写时复制的容器。
当我们往容器添加元素的时候,先将当前容器进行Copy,复制出一个新的容器,
然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。
1 | java复制代码 |
更新记录
- 20210727 : 修改格局
致谢
1 | java复制代码 |
本文转载自: 掘金