一.简介
队列:先进先出
入队:数据插入列表的尾部
出队:从队列头部取出一个数
二.普通队列
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