实现普通队列
使用循环数组
public class MyQueue {
private int[] array;
int head;//头指针
int tail;//尾指针
int size;//统计数组中元素个数
public MyQueue(){
this.array=new int[10];
this.head=0;
this.tail=0;
size=0;
}
//入队
public void add(int value){
if(size>=array.length){
return;//如果数组满了
}
array[tail++]=value;//加入元素,尾指针++
if(tail>=array.length){
//如果尾指针到数组最后位置,但是数组还没满,让尾指针直到数组第一个位置
tail=0;
}
size++;
}
//出队
public Integer tack(){
if(size==0){
return null;
}
int ret=array[head++];//先进先出,所以出队的是头节点,head++
if(head>=array.length){
//如果头指针到数组最后位置,但是数组还没空,让头指针直到数组第一个位置
head=0;
}
size--;
return ret;
}
}
实现阻塞队列
什么是阻塞队列?
1.线程安全
2.带有阻塞功能
a.如果队列满了,继续入队列,入队列操作就会阻塞等待,直到队列不为满。
b.如果队列空了,继续出队列,出队列操作就会阻塞等待,直到队列不为空
public class MyBlockQueue {
private int[] array;
//volatile修饰变量防止内存可读性问题,指令重排序问题
public volatile int head;
public volatile int tail;
public volatile int size;
public MyBlockQueue() {
this.array = new int[10];
this.head = 0;
this.tail = 0;
this.size = 0;
}
//入队
public void add(int value) throws InterruptedException {
//在多线程下,会抢占式执行,需要加锁保证原子性,由于下面操作全涉及修改都可能出现线程不安全,所以把修改部分全锁起来
synchronized (this){
while(size>=array.length){//第一次是判断阻塞队列是否满了,后面的判断是因为wait被唤醒后不一定非空,
// 例如可能不是notify唤醒的而是interrupt唤醒
this.wait();//阻塞等待,直到tack中取走元素,notify唤醒wait
}
array[tail++]=value;
if(tail>=array.length){
tail=0;
}
size++;
this.notify();//用来唤醒tack中的wait,并且不会同时发生阻塞等待,因为不可能同时又是满的又是空的
}
}
//出队
public int tack() throws InterruptedException {
synchronized (this){
while(size==0){
//阻塞等待,直到add中加入元素,add函数中的notify唤醒wait
this.wait();
}
int ret=array[head++];
if(head>=array.length){
head=0;
}
size--;
this.notify();
return ret;
}
}
}
阻塞队列的应用场景
”生产者消费者模型“描述的是多线程协同工作的一种方式
1.使用阻塞队列,有利于“解耦合”
避免了A B直接通信(此时耦合度比较高),由于阻塞队列的存在,A和B不需要知道彼此的存在,只要通过阻塞队列交互。
2.削峰填谷
阻塞队列就相当于咱们的三峡水库,当流量突然增加的时候,A服务器和队列承受了压力,B服务器还是按照原来的节奏消费数据,对B冲击就不大。如果没有阻塞队列一个服务器同时收到很多请求,每个请求要消耗一定的硬件资源,一旦资源耗尽,机器就会挂了