本文旨在 讨论阻塞队列 以及自定义阻塞队列 及其相关原理
1.基本概念
阻塞队列是一种更复杂的队列——可以保证线程安全
阻塞特性
- 队列为空时,尝试出队列,出队列操作会阻塞,阻塞到 其他线程添加元素为止
- 队列为满时,尝试入队列,入队列操作会阻塞,阻塞到 其他线程拿走元素为止
- 阻塞队列 一个最主要的应用场景,就是实现“生产者消费者模型”——一种特殊的编码技巧
例如:包饺子 擀饺子皮 的人属于 生产者 用饺子皮包饺子的人属于 消费者
饺子皮属于资源 放饺子皮的菜板 属于生产消费的交易场所——此处的交易场所就是阻塞队列
阻塞——是极端情况生产者,消费者之间速度不协调的情况
1.1生产者 消费者模型优势
1.解耦合(不一定是两个线程之间 也可以是两个服务器)

如果A B之间直接访问,那么AB的耦合就更高
通过使用阻塞队列 作为中间载体 可以降低耦合,降低后续修改成本

2.削峰填谷
如果出现上游服务器A 流量激增,为了控制节奏,先放入阻塞队列,高峰期 B服务器慢慢处理队列中的请求,低谷期时 可以赶紧处理剩余的请求。

1.2生产者消费者模型付出的代价
- 引入队列之后,整体结构会更加复杂,此时需要更多硬件资源,进行部署,生产环境的结构会更加复杂,难以管理。
- 效率会被影响
2.Java中ArrayList 和 LinkedList 有什么区别【经典面试题】
和顺序表 链表不同
顺序表:尾插尾删 O(1) 头插头删除 或 中间插入删除 O(N)
链表:任意位置插入删除 O(1)
但是在LinkedList类中 由于接口封装失误 无法发挥链表全部能力
LinkedList 执行add(插入的值,插入下标)
——寻找插入下标 需要从头找到指定下标位置,才能插入O(N) 中间位置删除,同理
3.使用循环队列实现阻塞队列
class MyBlockQueue{
private String[] date=null;
//队首
private int head=0;
//队尾
private int tail=0;
//队列元素个数
private int size=0;
public MyBlockQueue(int capcity){
date=new String[capcity];
}
public void put(String elem) throws InterruptedException {
synchronized (this) {
while (size>= date.length){
//队列满 需要进行阻塞
this.wait();
}
date[tail]=elem;
tail++;
if(tail>= date.length){
tail=0;
}
size++;
this.notify();
}
}
public String take() throws InterruptedException {
synchronized (this) {
while (size==0){
//队列空 需要进行阻塞
// return null;
this.wait();
}
String ret =date[head];
head++;
if(head>= date.length){
head=0;
}
size--;
this.notify();
return ret;
}
}
}

Put 中 this.wait() 当队列不满时 才进行唤醒——即其他线程成功执行take

take中 this.wait() 当队列不空时 才进行唤醒——即其他线程成功执行put

put 与 take 的等待 唤醒 是相对的
由图知 当t1.put 成功执行后 此刻队列非空 其他线程可以执行put 因此唤醒 take同理
为什么要使用while 判断size是否为零 而不是IF呢
由于wait 不仅仅会被notify 唤醒 还有可能因为Interrupt这样的方法中断
所以使用if作为wait的判断条件——存在wait被提前唤醒的风险
并且使用while 目的是二次验证 再次确认队列空不空
在官方文档中,wait设计时 就是搭配while使用
138

被折叠的 条评论
为什么被折叠?



