队列(queue)也是一种操作受限的线性表。他的操作特点是先进先出
- 队列有头节点
- 队列有尾节点
- 入队 enQueue(T t)只能增加到尾节点
- 出队 T dequeue() 只能从头节点出队
上面的四个特性决定了队列的先进先出性。在需要公平排队的情境下适合使用队列
队列也可以根据底层实现分为两种
- 底层是数组的顺序队列
- 底层是链表的链式队列
顺序队列的主要构造
- T[] data 存储数据的数组
- int size 队列的大小
- int head 队列的头下标
- int tail 队列的尾下标
如果要enQueue(T t)操作,把tail下标指定的尾节点的数组位置赋值成T,然后tail+1;
如果要deQueue()操作,返回head下标指定的数据,然后head+1;
上面的两个基本操作会遇到一个问题,如果一直enQueue(T t)tail很快就会到达数组的最大值,这个时候有两种选择:
- 拒绝入队
- 扩展队列数组
如果选择扩展队列,相当于申请一个1.5倍的数组,然后把head-》tail的数据全部复制过去
如果一直deQueue也会遇到一个问题,head会很快追上tail。这个时候队列又为空。但是head之前的数据位置还能重复使用,
这个时候其实可以用复制的方法,把head->tail的数据一起复制到从0开始的位置。
为了免除上面head->tail之后的复制操作,我们引入循环队列来解决这个问题,但是要注意判断循环队列满的情况
计算公式:(tail+1)%size = head
如果我们使用链式队列,就不会担心这个问题,链表可以无限延长,但是我们也要注意链表的长度,否则会内存被占用的太厉害。
- 队列为空的时候无法出队
- 队列满的时候无法入队
当无法操作出队入队的时候,线程阻塞,直到别的线程唤醒阻塞的线程。当操作线程出现阻塞的时候叫阻塞队列
多个线程同时出队入队操作并且线程安全的队列叫 并发队列
在开发中会遇到很多需要用到队列的情况,最主要的场景就是消费者模式,生产者入队,消费者出队
基于消费者模式,有多个实际应用场景,比如mq消费,线程池资源等