06.队列

作者: 学海一乌鸦 | 来源:发表于2020-05-23 15:54 被阅读0次

    1.定义

    一种操作受限的数据结构,只支持入列(enqueue)和出列(dequeue)

    2.实现

    用数组来实现

    public class ArrayQueue {
    
        // 队列的长度
        private int len;
        // 队尾
        private int tail;
        // 队头
        private int head;
        // 数组
        int[] arr;
    
        public ArrayQueue(int len) {
            this.len = len;
            this.tail = 0;
            this.head = 0;
            arr = new int[len];
        }
    
        // 队列中加元素
        public boolean enqueue(int item) {
            if (tail == len) {
                // 说明队列已经满了
                if (head == 0) {
                    return false;
                }
                // 搬移,将head-tail的元素搬迁到 0-(tail-head)
                for (int i = head; i < tail; i++) {
                    arr[i - head] = arr[i];
                }
                // 更新搬移后的tail和head
                head = 0;
                tail = tail - head ;
            }
            arr[tail++] = item;
            return true;
        }
    
        // 出列也是从最后面先出
        public int dequeue() {
            // 如果二者相等,说明没有元素了
            if (head == tail) {
                return null;
            }
            int ret = arr[head];
            head++;
            return ret;
        }
    }
    
    image.png

    用链表来实现

    public class LinkedQueue {
    
        // 尾结点
        Node tailNode = null;
        // 头结点
        Node headNode = null;
    
        // 入队
        public boolean enqueue(int item) {
            if (tailNode == null) {
                headNode = tailNode = new Node(item, null);
            } else {
                tailNode.next = new Node(item, null);
                tailNode = tailNode.next;
            }
            return true;
        }
    
        // 出队
        public int dequeue() {
            if (headNode == null) {
                return null;
            }
            int ret = headNode.data;
            headNode = headNode.next;
            //注意更新一下tailhead
            if (headNode == null) {
                tailNode = null;
            }
            return ret;
        }
    
        class Node {
            private int data;
            private Node next;
    
            public Node(int data, Node next) {
                this.data = data;
                this.next = next;
            }
        }
    }
    
    image.png

    循环队列

    • 不需要在tail ==n的时候搬移数据;
    • 但是会少存储一个数据单元,tail所指向的元素为空;
    • 判断条件: (tail==head)为空 ((tail+1)%n==head)满


      image.png
    
    public class CircularQueue {
        // 初始数组,假定为int类型
        int[] arr;
        // 容量
        int size;
        // 头
        int head;
        // 尾
        int tail;
    
        // 初始化
        public CircularQueue(int size) {
            this.size = size;
            arr = new int[size];
            head = 0;
            tail = 0;
        }
    
        public boolean enQueue(int item) {
            // 队列已经满的标志
            if ((tail + 1) % size == head) {
                return false;
            }
            arr[tail] = item;
            // 对于tail增加要考虑
            tail = (tail + 1) % size;
            return true;
        }
    
        public int deQueue() {
            // 表示队列已经满
            if (tail == head) {
                return null;
            }
            int ret = arr[head];
            head = (head + 1) % size;
            return ret;
        }
    }
    

    3.用途

    3.1 阻塞队列

    在队列的基础上加了阻塞的操作,如果入队时候没有空余位置或者出队的时候没有元素,那么就阻塞等待。
    利用此可以实现一个生产者-消费者模型:基于阻塞队列实现的“生产者 - 消费者模型”,可以有效地协调生产和消费的速度。当“生产者”生产数据的速度过快,“消费者”来不及消费时,存储数据的队列很快就会满了。这个时候,生产者就阻塞等待,直到“消费者”消费了数据,“生产者”才会被唤醒继续“生产”。


    image.png

    3.2并发队列

    • 最简单直接的实现方式是直接在 enqueue()、dequeue() 方法上加锁,但是锁粒度大并发度会比较低;
    • 基于数组的循环队列,利用 CAS 原子操作,可以实现非常高效的并发队列。这也是循环队列比链式队列应用更加广泛的原因
      考虑使用CAS实现无锁队列,则在入队前,获取tail位置,入队时比较tail是否发生变化,如果否,则允许入队,反之,本次入队失败。出队则是获取head位置,进行cas。

    3.3 线程池设计

    当我们向固定大小的线程池中请求一个线程时,如果线程池中没有空闲资源了,这个时候线程池如何处理这个请求?是拒绝请求还是排队请求?各种处理策略又是怎么实现的呢?

    • 一种是直接拒绝请求;
    • 阻塞排队,当有线程资源的时候再进行分配。做到公平的先来后到,那么可以使用队列。
      • 基于链表的实现,无界队列,导致过多的请求排队,从而是请求响应的时间比较长,不适合对时间敏感的系统;
      • 基于数组的实现,有界队列,队列已满的时候就会拒绝请求,是和对时间名的系统;

    实际上,对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队

    4.小结

    相关文章

      网友评论

          本文标题:06.队列

          本文链接:https://www.haomeiwen.com/subject/diffahtx.html