java数据结构----队列(一)之队列概述

一. 概述

1.队列的定义

  队列(queue)是一种特殊的线性表,队列就像一支队伍,队首(head)队尾(tail)以及队列长度。队列和栈类似,也是一个遵循特殊规则约束的数据结构。栈是一个后进先出(LIFO)的数据结构,队列正好与之相反,是一个先进先出(FIFO,First In First Out)的数据结构,例如我们去食堂就餐排队,先排上队的肯定先拿到餐出队,这和我们对列认知是一致的。

上面说到队列是一个遵循特殊规则的数据结构,除了先进先出,队列的插入只能从队列的一端操作,我们称这端为队尾;对应的,移除只能从另一端出来,我们称之为队首。将没有元素的队列称之为空队,往队列中插入元素的过程称之为入队,从队列中移除元素的过程称之为出队。

二.队列分类

根据不同的划分方式,队列有不同的分类 

1.根据有界无界划分, 可分为有界队列和无界队列

(1) 有界队列

    就是有固定大小的队列。比如设定了固定大小的 LinkedBlockingQueue,又或者大小为 0,只是在生产者和消费者中做中转用的 SynchronousQueue。常见的有界队列为:

  • 1)ArrayBlockingQueue 基于数组实现的阻塞队列
  • 2)LinkedBlockingQueue 其实也是有界队列,但是不设置大小时就是无界的。
  • ArrayBlockingQueue 与 LinkedBlockingQueue 对比:ArrayBlockingQueue 实现简单,表现稳定,添加和删除使用同一个锁,通常性能不如后者;LinkedBlockingQueue 添加和删除两把锁是分开的,所以竞争会小一些
  • 3)SynchronousQueue 比较奇葩,内部容量为零,适用于元素数量少的场景,尤其特别适合做交换数据用,内部使用 队列来实现公平性的调度,使用栈来实现非公平的调度,在Java6时替换了原来的锁逻辑,使用CAS代替了。

(2) 无界队列

    指的是没有设置固定大小的队列。这些队列的特点是可以直接入列,直到溢出。当然现实几乎不会有到这么大的容量(超过 Integer.MAX_VALUE),所以从使用者的体验上,就相当于 “无界”。比如没有设定固定大小的 LinkedBlockingQueue。常见的无界队列为:

  • 1)ConcurrentLinkedQueue 无锁队列,底层使用CAS操作,通常具有较高吞吐量,但是具有读性能的不确定性,弱一致性: 不存在如ArrayList等集合类的并发修改异常,通俗的说就是遍历时修改不会抛异常
  • 2)PriorityBlockingQueue 具有优先级的阻塞队列
  • 3)DelayedQueue 延时队列,使用场景, 缓存:清掉缓存中超时的缓存数据任务超时处理, 补充:内部实现其实是采用带时间的优先队列,可重入锁,优化阻塞通知的线程元素leader
  • 4)LinkedTransferQueue 简单的说也是进行线程间数据交换的利器,在SynchronousQueue 中就有所体现

2.根据存储结构划分

(1) 链式队列

     链式队列是队列的实现方式之一。链式队列内部使用带头结点的单向链表来实现。它的好处的是灵活,队列容量理论上是不受限制的。

(2) 循环队列(一种顺序存储结构)

      循环队列是顺序存储结构(以数组的形式实现);顺序存储就是指用一组连续的存储单元依次存储,链式存储内存中地址不是挨着的,循环队列增设了两个指针头指针和尾指针,实现空间的最大利用。为充分利用向量空间,克服"假溢出"现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。循环队列是把顺序队列首尾相连,把存储队列元素的表从逻辑上看成一个环,成为循环队列。

(3)单向队列Queue

    Queue extends Collection 队列实现了 Collection 接口,Collection接口是集合类的顶级接口. 队列是只允许在一端进行插入操作,在另外一段进行删除操作的线性表,队列不允许在中间部位进行操作,先进先出

(4) 双端队列Deque(又叫双向队列)

    双向队列在java中的接口规范是Deque,Deque继承Queue,Queue继承Collection.  双端队列是一种两端都可以 删除元素和追加元素的线性结构。双端队列比普通的队列更加灵活。双端队列可以用双向链表实现。

(5) 优先队列

   优先队列(priority queue)为一种不必遵守队列特性--FIFO的有序表,其中的每一个元素都赋予一个优先权(Priority),加入元素时可任意加入,但有最高优先权者(Highest Priority Out First, HPOF)则最先输出。在计算机中CPU的工作调度,优先权调度(Priority Scheduling, PS)就是一种挑选任务的“调度算法”(Scheduling Aalgotithm),也会使用到优先队列。级别高的用户,就比一般用户拥有较高的权利。

3.根据阻塞和非阻塞划分

(1) 阻塞队列

   阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作支持阻塞的插入和移除方法。

  • 1)支持阻塞的插入方法:意思是当队列满时,队列会阻塞插入元素的线程,直到队列不满。
  • 2)支持阻塞的移除方法:意思是在队列为空时,获取元素的线程会等待队列变为非空。

阻塞队列常用于生产者和消费者的场景,生产者是向队列里添加元素的线程,消费者是从队列里取元素的线程。阻塞队列就是生产者用来存放元素、消费者用来获取元素的容器。

java.util.concurrent 中加入了 BlockingQueue 接口和五个阻塞队列类

     LinkedBlockingQueue的容量是没有上限的(说的不准确,在不指定时容量为Integer.MAX_VALUE,不要然的话在put时怎么会受阻呢),但是也可以选择指定其最大容量,它是基于链表的队列,此队列按 FIFO(先进先出)排序元素。
     ArrayBlockingQueue在构造时需要指定容量, 并可以选择是否需要公平性,如果公平参数被设置true,等待时间最长的线程会优先得到处理(其实就是通过将ReentrantLock设置为true来 达到这种公平性的:即等待时间最长的线程会先操作)。通常,公平性会使你在性能上付出代价,只有在的确非常需要的时候再使用它。它是基于数组的阻塞循环队 列,此队列按 FIFO(先进先出)原则对元素进行排序。
    PriorityBlockingQueue是一个带优先级的 队列,而不是先进先出队列。元素按优先级顺序被移除,该队列也没有上限(看了一下源码,PriorityBlockingQueue是对 PriorityQueue的再次包装,是基于堆数据结构的,而PriorityQueue是没有容量限制的,与ArrayList一样,所以在优先阻塞 队列上put时是不会受阻的。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会导致 OutOfMemoryError),但是如果队列为空,那么取元素的操作take就会阻塞,所以它的检索操作take是受阻的。另外,往入该队列中的元 素要具有比较能力。
     DelayQueue(基于PriorityQueue来实现的)是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。

(2) 非阻塞队列

    ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列,它采用先进先出的规则对节点进行排序,当我们添加一个元素的时候,它会添加到队列的尾部;当我们获取一个元素时,它会返回队列头部的元素。入队和出队操作均利用CAS(compare and set)更新,这样允许多个线程并发执行,并且不会因为加锁而阻塞线程,使得并发性能更好。


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值