队列的实现(循环队列Disruptor)

14 篇文章 0 订阅

一.简介

队列:先进先出
入队:数据插入列表的尾部
出队:从队列头部取出一个数

二.普通队列

package leetcode.editor.cn;

import java.util.Arrays;

public class ArrayQueue {

    public int[] arr;
    public int size = 0;
    public int head = 0, tail = 0;

    public ArrayQueue(int size) {
        this.arr = new int[size];
        this.size = size;
    }

    public boolean enqueue(Integer item) {
        // tail == n表示队列末尾没有空间了
        if (tail == size) {
            return false;
        }
        arr[tail] = item;
        ++tail;
        return true;

    }

    public int dequeue() {
        if (head == size) {
            return -1;
        }
        int val = arr[head];
        arr[head] = 0;
        head++;
        return val;
    }

    public static void main(String[] args) {
        int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        ArrayQueue queue = new ArrayQueue(10);
        for (int i = 0; i < arr.length; i++) {
            queue.enqueue(arr[i]);
        }
        for (int i = 0; i < 3; i++) {
            queue.dequeue();
        }
        print(queue);
        queue.enqueue(99);
        print(queue);
        queue.enqueue(88);
        print(queue);
        queue.enqueue(77);
        print(queue);
        queue.enqueue(66);
        print(queue);
        queue.enqueue(55);
        print(queue);
    }

    public static void print(ArrayQueue queue) {
        Arrays.stream(queue.arr).forEach(c -> {
            System.out.print(c + " ");
        });
        System.out.println();
    }
}

输出结果如下:
在这里插入图片描述
出队了三个元素,结果还是插入不了,插入时,队列的判断条件不对,只有 tail == size && head==0,表示整个队列都占满了。

    public boolean enqueue(Integer item) {
        // tail == n表示队列末尾没有空间了
        if (tail == size) {
            // tail ==n && head==0,表示整个队列都占满了
            if (head == 0) {
                return false;
            }
            // 数据搬移
            for (int i = head; i < tail; ++i) {
                arr[i - head] = arr[i];
                arr[i] = 0;
            }
            // 搬移完之后重新更新head和tail
            tail -= head;
            head = 0;
        }
        arr[tail] = item;
        ++tail;
        return true;
    }

把元素都往前面移动head个位置,然后才能正常的插入
在这里插入图片描述

三.循环队列

我们刚才用数组来实现队列的时候,在tail==size时,会有数据搬移操作,这样入队操作性能就会受到影响.

队列满:(tail+1)%size=head
队列满时,tail指的位置没有存数据,会浪费一个存储空间

在这里插入图片描述

   /**
     * 会浪费一个存储空间,比如size=10,只能存9个元素
     * @param item
     * @return
     */
    public boolean cycleEnQueue(Integer item) {
        // tail+1)%size==head表示队列末尾没有空间了
        if ((tail+1)%size==head) {
            return false;
        }
        arr[tail] = item;
        tail=(tail+1)%size;
        return true;

    }

    public int cycleDequeue() {
        if (head == tail) {
            return -1;
        }
        int val = arr[head];
        arr[head] = 0;
        head=(head + 1) % size;
        return val;
    }

size=10,只能插入9个元素,99先插入后面,然后循环一圈,88插入第一个位置,77插入第二个位置,这个时候,队列满了,就不能插入元素了。
在这里插入图片描述

四.Disruptor

优点:

      1.没有锁(锁涉及操作系统),使用CAS(CAS是CPU级别,更快)
      2.RingBuffer的数据结构,只需要一个序列号来保存下一个可用的空间,减小竞争
      3.消除了伪共享

上面的循环队列在多线程的情况下,会遇到数据被覆盖的问题。
因为arr[tail] = item;和tail=(tail+1)%size; 这两步操作不是原子性操作。要解决这个问题,就需要对这两步加锁,但这样会降低性能

在这里插入图片描述

RingBuffer

在这里插入图片描述

  • 1.消费者比生产快
    消费者需要停下来,等生产者有新的数据,消费者才能继续消费
  • 2.消费者比生产慢
    数组的长度是有限的,如果大小为10的数组,生产者到末尾的时候,序号为10的空间已经放完了,下一个要放到序号为11的空间(也就是数组1的位置),但第1号的数据,消费者还没取走,生产者不能覆盖1号的数据,只能等消费者取走后,才能继续

在这里插入图片描述

要找到数组中的数据,用取模的方式,序号%size,比如序号12,那么就是12%10=2,序号12指向的是数组2号位置。


RingBuffer的大小:2的n次方,更利于计算机二进制计算

伪共享

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
参考:
https://developer.aliyun.com/article/62865

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值