阿华代码,不是逆风,就是我疯,你们的点赞收藏是我前进最大的动力!!希望本文内容能够帮助到你!
目录
一:阻塞队列
1:概念
2:阻塞队列与普通队列比较
二:“生产者消费者模型”——包饺子
1:包饺子流程
2:分工协作
(1)解释
(2)问题
三:“生产者消费者模型”——分布式系统
1:分布式模型
2:队列阻塞优化
3:缺点
4:优点
①解耦合
②缓冲压力(削峰填谷)
四:自己实现一个阻塞队列
前引:库中自带阻塞队列的数据结构
编辑
1:自己实现的一个队列
2:线程安全问题
(1)问题一
(2)阻塞队列部分怎么写
①用wait
②唤醒问题
③解决方法:
一:阻塞队列
1:概念
对于一个满的队列,入队操作就会陷入阻塞,直到这个队列有元素出队后,才可以往队列里面加入元素。
对于一个空的队列,出队操作就会陷入阻塞,直到这个队列有元素入队后,才可以对队列进行出队操作
2:阻塞队列与普通队列比较
阻塞队列在多线程中是比价安全的
二:“生产者消费者模型”——包饺子
1:包饺子流程
①和面(一般一个人即可,不适用于多线程)
②撵饺子皮
③包饺子
上述②③可以多线程进行
假设现在有三个滑稽老铁包饺子
假设,每个滑稽老铁拿到擀面杖,擀了一个皮,包了一个饺子,在这个过程中,滑稽老铁会争夺擀面杖(锁竞争),虽然比单线程快,但是效率还是很低
2:分工协作
(1)解释
上述图,一号老铁负责专门撵饺子皮(生产),2,3号负责包饺子,桌子(阻塞队列)负责传递饺子皮,大大提高了包饺子的效率。
(2)问题
①1号滑稽撵饺子皮的速度远远大于包饺子的速度,导致桌子上全是饺子皮,此时桌子就相当于队列阻塞
②1号滑稽撵饺子皮的速度远远小于包饺子的速度,导致桌子上是空的,2号3号空闲,此时桌子就相当于队列空
三:“生产者消费者模型”——分布式系统
1:分布式模型
通过上面的简述我们来看实际开发中是怎样的模型
实际开发过程中,服务器的整个功能往往是由多个服务器分工,它们彼此间通过网络通信进行交互。
2:队列阻塞优化
但在上述图中,A与B,A与C之间的耦合性比较强,有一个挂了,很可能就会影响到其它的服务器,于是我们引入队列阻塞
A和BCD之间不是直接交互了,而是通过队列这个中间桥梁进行交互,如果B挂了,也不会影响到ACD
3:缺点
①系统更复杂了,要维护的服务器变多
②效率降低,有阻塞队列这个中间商,必然会增加开销了
4:优点
①解耦合
②缓冲压力(削峰填谷)
四:自己实现一个阻塞队列
前引:库中自带阻塞队列的数据结构
在Java标准库中提供了三种现成的带有阻塞队列的数据结构
其中它们的入队列有两种方法put(自带阻塞效果)offer(不带有阻塞效果)
下面我们举例一种
ArrayBlockingQueue queue2 = new ArrayBlockingQueue<>(100);
try {
queue2.put("put方法不带阻塞功能");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
queue2.offer("offer方法带有阻塞功能");
try {
System.out.println(queue2.take());
System.out.println(queue2.take());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
1:自己实现的一个队列
package thread;
/**
* Created with IntelliJ IDEA.
* Description:
* User: Hua YY
* Date: 2024-09-25
* Time: 17:47
*/
class MyQueue{
String[] elems = null;
int head = 0;//记录出元素时
int tail = 0;//记录进元素时
int size = 0;//当前队列中有多少个元素
public MyQueue(int capacity){
elems = new String[capacity];
}
public void put(String elem){
if (size >= elems.length ){
//此时队列满了放不了要在这写阻塞
return;
}
elems[tail] = elem;
tail++;
if(tail >= elems.length){
tail = 0;
}
size++;
}
public String take(){
if(size <= 0){
return null;
}
String elem = elems[head];
head++;
if (head >= elems.length){
head = 0;
}
size--;
return elem;
}
}
public class ThreadDemon31 {
public static void main(String[] args) {
MyQueue queue = new MyQueue(100);
queue.put("aaa");
queue.put("bbb");
queue.put("ccc");
queue.put("ddd");
System.out.println("开始出队列");
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println(queue.take());
System.out.println(queue.take());
}
}
2:线程安全问题
(1)问题一
①打包成这样可以吗——不行,size最后会被写两遍
②解决方式:给整个put方法内部都加上synchronized
(2)阻塞队列部分怎么写
①用wait
②唤醒问题
可以看到上述图例:假设现在队列满,两个put都阻塞,take出了 一个元素,唤醒了第一个put,第一个put又唤醒了第二个put,这就出问题了
③解决方法:
详细看明白上面举的例子后继续~~
我们知道对于,wait(等待)和notify(唤醒)中间隔着的几秒,对于计算机来说,可能就会发生翻天覆地的变化。这个if条件句就是,被唤醒后,其实现状不一定像一开始满足这个if条件句了
于是我们引入替换成——while语句,句内的wait被唤醒后会再一次进行条件判断,如果此时条件,不满足,会再一次的陷入wait等待。
在java编译器中,也是推荐wait和while循环配套使用。