目录
1.阻塞队列
1.举例:包饺子
1.通过多线程来实现
2.通过阻塞队列来实现
2.消息队列
1.解耦
2.削峰填谷
用消息队列来解决
3.异步操作
3.实现一个阻塞队列
使用循环数组
4.实现生产者和消费者模型
完整代码
5.虚假唤醒
1.概念及原因
2.解决方法
1.阻塞队列
阻塞队列也是一种队列,也满足队列的特性,先进先出
- 入队元素时,判断队列是否已满,若满了就阻塞等待,等有位置在插入
- 出队元素时,判断队列是否为空,若为空就阻塞等待,等有元素再取出
1.举例:包饺子
- 和面 -> 大前提,只需要做一次(单例模式)
- 擀皮
- 包饺子
1.通过多线程来实现
2.通过阻塞队列来实现
2.消息队列
本质上就是一个阻塞队列,在此间基础上为放入阻塞队列的消息打一个标签,消息的标签可以实现分组的作用
1.解耦
在设计程序的时候提出过一些要求,比如:高内聚,低耦合
- 高内聚:是一种组织代码的方式,把功能强相关的代码写在一起,方便后期维护,增加代码的复用性
- 低耦合:不要把相同的代码写的到处都是,一般通过抽象的方式把代码封装成方法,直接调用即可
良好的代码组织方式,可以有效地降低成本
举例:购物支付过程中的订单,支付以及物流
解决方案
2.削峰填谷
峰与谷指的是消息的密集程度
- 在流量高峰的时候用消息队列缓冲
- 在流量下降后,再将消息队列积攒的消息慢慢解决
比如在上述订单中,双十一这种日子订单量会暴增,此时我们增加多台服务器去维护显然并不符合常理,因为一年就只在这一天需要这么多服务器,准备太多会增加大量成本以及耗费资源
用消息队列来解决
3.异步操作
- 同步:请求当必须死等对方的响应
- 异步:发出请求之后,自己去干别的事情,有响应时会接收到通知从而处理响应
3.实现一个阻塞队列
- 实现一个普通队列,底层用到了两种数据结构,一个是链表,一个是循环数组
- 阻塞队列就是在普通的队列基础上加入了阻塞等待的操作
使用循环数组
public class MyBlockingQueueforList {
private volatile Node head;
private volatile int size = 0;
public volatile int compacty = 3;
public class Node{
int val;
Node next;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("[");
Node prev = head;
while (prev != null) {
sb.append(prev.val);
if(prev.next != null) {
sb.append("->");
}
prev = prev.next;
}
sb.append("]");
return sb.toString();
}
public MyBlockingQueueforList(int compacty) {
this.compacty = compacty;
}
public MyBlockingQueueforList() {};
public void put(int val) throws InterruptedException {
synchronized (this) {
if (size == compacty); {
this.wait();
}
Node prev = head;
for (int i = 0; i < size; i++) {
prev = prev.next;
}
prev.val = val;
size++;
this.notify();
}
}
public int take() throws InterruptedException {
synchronized (this) {
while (size == 0) {
this.wait();
}
int val = head.val;
head = head.next;
size--;
this.notify();
return val;
}
}
}
4.实现生产者和消费者模型
完整代码
import java.util.concurrent.TimeUnit;
public class practice01 {
private static MyBlockingQueue queue = new MyBlockingQueue(3);
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
int num = 1;
while (true) {
System.out.println("生产了消息"+num);
try {
queue.put(num);
num++;
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread t2 = new Thread(() -> {
while (true) {
try {
int num = queue.take();
System.out.println("减少了消息"+num);
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
t2.start();
}
}
5.虚假唤醒
1.概念及原因
2.解决方法
最后别忘了给共享变量加volatile