阻塞队列
文章目录
- 阻塞队列
- 1.队列的概念
- 2.阻塞队列
- 3.现实中的例子
- 4.消息队列
- 5.使用队列的优势
- 1.解耦
- 2.削峰填谷
- 3.异步操作
- 6.实现
1.队列的概念
一种先进先出的数据结构
2.阻塞队列
队列写元素是从队尾插入,从对头取出
当插入元素时,先判断一下队列是否已满,如果满了就等待(阻塞),等到队列中有空余位置时再插入
当取出元素时,先判断一下队列是否为空,如果空了就等待(阻塞),等到队列中有元素时再去取出
3.现实中的例子
包饺子:
- 和面
- 擀皮
- 包饺子
两种分配方式
- 各干各的,每个人都擀皮,包饺子,这是会产生严重的锁竞争
- 一个人专门负责擀皮,其他人包饺子,常用的方式,提高效率
这里涉及到一个模型,生产者消费者模型
擀皮的那个人是生产者,其他包饺子的人是消费者,放饺子皮的地方是交易场所
生产者消费者模型利用阻塞队列解决了锁竞争可能产生的冲突的问题,而且还有其他优势
4.消息队列
在阻塞队列的基础上对消息进行分组
5.使用队列的优势
1.解耦
高内聚,低耦合
高内聚:业务强相关的功能或代码组织在一起,不要在这个类里一个方法,那个类里一个方法,为了后续维护方便,设计与组织的一种方式
低耦合:不强相关的代码,或是重复代码,尽量抽象成其他的接口,在各个方法中调用就可以了。
2.削峰填谷
削峰:在流量暴增的时候用消息队列把消息缓存起来,后面的服务器一点一点按正常速度处理
提谷:消费消息的服务器在流量不多的情况下,处理之前堆积的消息,这个就是填谷
例如:淘宝双十一,订单消息量过大,物流、银行等服务不能及时处理,消费服务器就可以按照其他业务的API流量限制处理消息
3.异步操作
同步是指的是一次只能完成一件任务。如果有多任务,就必须排队,前面一个任务完成,再执行后面一个任务
异步指的是每一个任务有一个或多个回调函数(callback),前一个任务结束后,不是执行后一个任务,而是执行回调函数,后一个任务则是不等前一个任务结束就执行,所以程序的执行顺序与任务的排列顺序是不一致的、异步的。
6.实现
在JDK中为我们提供了关于阻塞队列的接口BlockingQueue
这里我们通过一个数组来实现阻塞队列
public class MyBlockingQueue {
//用数组来保存数据
private Integer[] elementData=new Integer[1000];
//队尾与队首的下标
private volatile int head=0;
private volatile int tail=0;
//有效元素个数
private volatile int size=0;
/**
* 添加元素
* @param value
*/
public void put(Integer value) throws InterruptedException {
synchronized (this){
//先判断队列是否满了
//解决虚假唤醒问题
while (size>=elementData.length){
//队列已满就阻塞等待
this.wait();
}
//从队尾入队
elementData[tail]=value;
//队尾下标向前移动
tail++;
if(tail>=elementData.length){
tail=0;
}
//修改有效个数
size++;
//添加新元素之后,唤醒其他线程
this.notifyAll();
}
}
/**
* 获取元素
* @return
*/
public Integer take() throws InterruptedException {
synchronized (this){
//先判断队列是否为空
while (size == 0) {
//出队时,如果为空就等待
this.wait();
}
//出队队首元素
Integer value=elementData[head];
//向后移动head
head++;
if (head>= elementData.length){
head=0;
}
//修改有效元素的个数
size--;
//出队时唤醒其他线程
this.notifyAll();
return value;
}
}
}