阻塞队列优点1:让多个服务器之间充分的解耦合
阻塞队列同样也是一个符合先进先出规则的队列
生产者消费者模型,是实际开发中非常有用的一种多线程开发手段!!!尤其是在服务器开发的场景中
假设,有两个服务器, AB,A作为入口服务器直接接收用户的网络请求,B作为应用服务器,来给A提供一些数据
如果不使用生产者消费者模型此时A和B的耦合性是比较强的!!在开发A代码的时候就得充分了解到B提供的一些接口.开发B代码的时候也得充分了解到A是怎么调用的—旦想把B换成C,A的代码就需要较大的改动,而且如果B挂了,也可能直接导致A也顺带挂了.
使用生产者消费者模型,就可以降低这里的耦合
对于请求:A是生产者,B是消费者.对于响应:A是消费者,B是生产者.阻塞队列都是作为交易场所
A只需要关注如何和队列交互,不需要认识BB也只需要关注如何和队列交互,也不需要认识A队列是不变~~
如果B挂了,对于A没啥影响~~如果把B换成C,A也完全感知不到…
阻塞队列优点2:可以对请求进行削峰填谷
A暴涨=>B暴涨
A作为入口服务器,计算量很轻,请求暴涨,问题不大.
B作为应用服务器,计算量可能很大,需要的系统资源也更多.如果请求更多了,需要的资源进—步增加,如果主机的硬件不够,可能程序就挂了
如果使用阻塞队列:
"削峰”这种峰值很多时候不是持续的,就一阵,过去了就又恢复了
"填谷”B仍然是按照原有的频率来处理之前积压的数据
A请求暴涨=>阻塞队列的请求暴涨,由于阻塞队列没啥计算量,就只是单纯的存个数据,就能抗住更大的压力.
B这边仍然按照原来的速度来消费数据,不会因为A的暴涨而引起暴涨.B就被保护的很好,就不会因为这种请求的波动而引起崩溃.
手动实现一个阻塞队列
java标椎库中提供的的阻塞队列
队列可以基于数组实现, 也可以基于链表实现.
此处基于数组实现阻塞队列更简单,就直接写数组版本
class MyBlockingQueue {
// 保存数据的本体
private int[] data = new int[1000];
// 有效元素个数
private int size = 0;
// 队首下标
private int head = 0;
// 队尾下标
private int tail = 0;
// 专门的锁对象
private Object locker = new Object();
// 入队列
public void put(int value) throws InterruptedException {
synchronized (locker) {
if (size == data.length) {
// 队列满了. 暂时先直接返回.
// return;
locker.wait();
}
// 把新的元素放到 tail 位置上.
data[tail] = value;
tail++;
// 处理 tail 到达数组末尾的情况
if (tail >= data.length) {
tail = 0;
}
// tail = tail % data.length;
size++; // 千万别忘了. 插入完成之后要修改元素个数
// 如果入队列成功, 则队列非空, 于是就唤醒 take 中的阻塞等待.
locker.notify();
}
}
// 出队列
public Integer take() throws InterruptedException {
synchronized (locker) {
if (size == 0) {
// 如果队列为空, 就返回一个非法值.
// return null;
locker.wait();
}
// 取出 head 位置的元素
int ret = data[head];
head++;
if (head >= data.length) {
head = 0;
}
size--;
// take 成功之后, 就唤醒 put 中的等待.
locker.notify();
return ret;
}
}
}
public class Demo22 {
private static MyBlockingQueue queue = new MyBlockingQueue();
public static void main(String[] args) {
// 实现一个简单的生产者消费者模型
Thread producer = new Thread(() -> {
int num = 0;
while (true) {
try {
System.out.println("生产了: " + num);
queue.put(num);
num++;
// 当生产者生产的慢一些的时候, 消费者就得跟着生产者的步伐走.
// Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
producer.start();
Thread customer = new Thread(() -> {
while (true) {
try {
int num = queue.take();
System.out.println("消费了: " + num);
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
customer.start();
}
}