队列

0x00 基本概念

和栈一样,队列也是一种受操作限制的线性表,和栈相反的是队列是先进先出,最基本的操作也是两个,入队和出队。

队列应用非常广泛,特别是一些具有额外特性的队列,比如:循环队列、阻塞队列、并发队列等。
高性能队列Disruptor、linux环形缓存都用到了循环并发队列,java concurrent并发包利用ArrayBlockingQueue来实现公平锁

0x01 顺序队列&链式队列&循环队列

和栈一样,队列也可以使用数组或链表来实现,数组实现的队列叫顺序队列,链表实现的叫链式队列

  • 顺序队列

由于数组长度不能修改,所以顺序队列大小也要提前给出,队列需要维护两个指针,分别指向队首和队尾,当队尾到达数组最后时,队首由于出队操作可能不在数组头部,这时数组前面有部分空间是没有使用的,这时需要把所有数据前移,这一步可以放在每次入队的时候判断,集中搬迁

队空判断:head==tail
队满判断:tail==n && head==0

GitHub

  • 链式队列

链式队列使用链表实现,也是两个指针head、tail分别指向队首和队尾,出队时返回head指向的元素,然后将head指向head的next,入队时,将元素插入tail的next,然后将tail指向next,注意队列为空时的判断,还有当只剩最后一个元素时,出队,此时head指向null,要注意修改tail的指向

队空判断:head==null
队满判断:不用判断,注意一下tail==null的情况就行了

GitHub

  • 循环队列

循环队列看起来像一个环,没有首尾,和数组比,没有了首尾,这样我们可以避免数据的搬迁工作,循环队列的实现,最关键的是队空和队满时的判断,以数组实现为例:假设数组长度n、队首指针head(指向将要出队的位置)、队尾指针tail(指向将要入队的位置)

顺序队列中,tail也是指向将要入队的位置,当队列满时tail索引为n,且head索引为0,当队列空时head和tail索引相等。

在循环队列中,为空时,head和tail相等,队列满时,tail的下一个是head,为了与队空时区分,这里会浪费一个位置。所以当tail下一个位置为head时,就不在入队,

队空判断:队首指针和队尾指针相等 head==tail
队满判断:tail的下一个位置是head,数组索引时0~n-1,所以tail+1==head,还有一种特殊情况,tail=n-1,head=0,所以结合起来得到 (tail+1)%n == head

GitHub

0x02 课后思考

1、除了线程池这种池结构会用到队列排队请求,你还知道有哪些类似的池结构或者场景中会用到队列的排队请求呢?

实际上,对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过队列这种数据结构来实现,还有分布式消息队列,例如kafka也是一种队列

2、今天讲到并发队列,关于如何实现无锁并发队列,网上有非常多的讨论。对这个问题,你怎么看呢?

无锁队列可以使用cas(Compare and Swap)原子操作来实现,入队前获取tail位置,入队时比较tail是否变化,如果没变,则允许入队,反之入队失败,出队则是获取head位置,进行cas

你可能感兴趣的:(队列)