前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >高并发场景中的Queue集合

高并发场景中的Queue集合

作者头像
愿天堂没有BUG
发布2023-01-10 15:20:13
4480
发布2023-01-10 15:20:13
举报

概述

JUC中提供了大量的Queue/Deque集合,用于满足程序员在多种高并发场景中的数据管理和数据通信需求,常用的Queue/Deque集合如图9-1所示(后面多处使用队列称呼Queue/Deque集合,实际上只是从不同的维度称呼同样的事物)。

前面已经介绍了队列的基本工作特点:从队列的头部取出数据对象,并且在队列的尾部添加数据对象,也就是说,先进入队列的数据对象会先从队列中取出(先进先出,FIFO)。此外,图9-1中的队列都有一些自身的工作特点。

• ArrayBlockingQueue:这是一种内部基于数组的,在高并发场景中使用的阻塞队列,是一种有界队列。该队列的一个显著工作特点是,存储在队列中的数据对象数量有一个最大值。

• LinkedBlockingQueue:这是一种内部基于链表的,在高并发场景中使用的阻塞队列,是一种无界队列。该队列最显著的工作特点是它的内部结构是一个链表,这保证了它可以在有界队列和无界队列之间非常方便地进行转换。

• LinkedTransferQueue:这是一种内部基于链表的,可以在高并发场景中使用的阻塞队列,是一种无界队列。可以将它看成LinkedBlockingQueue队列和ConcurrentLinkedQueue队列优点的结合体,既能关注集合的读/写操作性能,又能维持队列的工作特性。在实际应用中,经常使用该队列进行线程间的消息同步操作。

• PriorityBlockingQueue:这是一种内部基于数组的,采用小顶堆结构的,可以在高并发场景中使用的阻塞队列,是一种无界队列。该队列最显著的工作特点是,队列中的数据对象按照小顶堆结构进行排序,从而保证从该队列中取出的数据对象是权值最小的数据对象。

• DelayQueue:这是一种内部依赖PriorityQueue的,采用小顶堆结构的,可以在高并发场景中使用的阻塞队列,是一种无界队列。该队列的一个显著工作特点是,队列中的数据对象除了会按照小顶堆结构进行排序外,这些数据对象还会通过实现java.util.concurrent.Delayed接口定义一个延迟时间,只有当延迟时间最小的数据对象的值都小于或等于0时(延迟时间会作为节点的权重值参与排序),该数据对象才会被外部调用者获得。

什么是有界队列,什么是无界队列• 有界队列:队列容量有一个固定大小的上限,一旦队列中的数据对象总量达到容量上限时,队列就会对添加操作进行容错性处理。例如,返回false,证明操作失败;抛出运行时异常;进入阻塞状态,直到操作条件满足要求。也就是说,不再允许立即添加数据对象了。

• 无界队列:队列容量没有一个固定大小的上限,或者容量上限值是一个很大的理论上限值(如常量Integer.MAX_VALUE的最大值为2 147 483647)。由于这种队列理论上没有容量上限,因此理论上调用者可以将任意数量的数据对象添加到集合中,而不会使添加操作出现容量异常。

无界队列是不是真的无界呢?显然不是的,根据上面的描述可知,一部分 无 界 队 列 是 可 以 在 进 行 实 例 化 时 设 置 其 队 列 容 量 上 限 的 。例 如 ,LinkedBlockingQueue队列默认的容量值是Integer.MAX_VALUE(相当于无界),但是我们也可以将LinkedBlockingQueue队列的容量值设置为一个特定的值。

此外,无界队列不能保证其容量无限大的另一个原因是JVM可管理的堆内存是有上限的,当超过堆内存容量且JVM无法再申请新的内存空间时,应用程序会抛出OutofMemoryError异常。

什么是阻塞队列,什么是非阻塞队列

我们知道,Queue接口是BlockingQueue接口的父级接口,前者定义了一些与队列有关的接口,后者在此基础上补充了一些接口功能,Queue接口的主要方法如下。

根据上述源码可知,java.util.Queue接口中主要定义了6个方法,这6个方法可以分为两类:一类是在操作时如果Queue集合的状态不符合要求,就会抛出异常的;另一类是在操作时如果集合的状态不符合要求,则尽可能不抛出异常的——通过返回一些特定的值进行替换。这6个方法的详细分类如表9-1所示。

这里要特别说明的情况是,由于这些方法的操作场景相似,只对异常抛出的要求或对返回值的要求做出了描述,因此在实现了BlockingQueue接口的具体集合中,通常可以看到这些方法间存在相互调用的情况。例如,在无界队列中,经常可以看到队列的add()方法直接调用了队列的offer()方法。在有界队列中,通常会对offer()方法返回false的情况进行抛出异常处理。例如,本书后面将要详细介绍的有界队列ArrayBlockingQueue,其内部的add()方法就对调用offer()方法返回值进行了特别判定,源码如下。

作为Queue接口的子级接口,BlockingQueue接口在Queue接口功能的基础上又提出了多个新的功能,源码如下。

可以发现以上方法的共同特点:在调用方法时,如果出于一些客观原因无法立即完成工作,那么调用方法的线程会进入阻塞状态,直到满足某种条件,才会退出阻塞状态(能够成功完成操作,或者达到阻塞最长等待时间,或者当前线程收到interrupt中断信号)。

在高并发场景中,Queue/Deque集合除了可以充当多线程间数据操作的载体,还可以主导线程间的数据协作工作。要完成数据传输的主导工作,这种集合就一定有对应的功能。因此,我们可以给阻塞队列一个通俗的描述,即阻塞队列是实现了j.u.c.BlockingQueue接口的队列,并且其能够提供这样一组方法功能:当调用者通过这组方法对队列进行读/写操作,发现不满足操作条件时,参与者所在线程会进入阻塞状态,直到满足某种条件,才会退出阻塞状态继续工作。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-01-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 愿天堂没有BUG 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档