生产者和消费者模式
生产者消费者模式(简易版)
-
synchronize 中的 wait 和 notifyAll 实现
- synchronize: 当一个线程进入到同步代码块时,会获取到当前的锁,而这时如果其他使用同样的锁的同步代码块也想执行内容,就必须等待当前同步代码块的内容执行完毕,在执行完毕后会自动释放这把锁,而其他的线程才能拿到这把锁并开始执行同步代码块里面的内容
-
生产者和消费者中间需要有"一条通道"来进行取和产的存放, 因此会用到数据结构的"队列",队列拥有先进先出的特性
-
队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作
生产者模式
- 代入厨师的生产模式:生产菜品需要时间,利用Thread.sleep方法进行睡眠(假设做菜时间)
- 利用synchronized是"谁拿到这个锁谁就可以运行它所控制的那段代码"
private static void Producer() {
while (true) {
try {
Thread.sleep(3000);
synchronized (queue){
String name = Thread.currentThread().getName();//获取线程名字
System.out.println(new Date() + " "+ name+" 出餐了!!!");
queue.offer(new Object());//增加菜品进队列
queue.notifyAll();//不止有一个客户队列
//唤醒queue.wait的线程, 通知队列客户可以取餐
//因为有好几个客户线程,几个客户线程的队列都可能为空然后被wait,所以需要唤醒几个客户线程的队列
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
消费者模式
- 代入客户的消费模式:客户取走餐并享用,需要Thread.sleep方法进行睡眠(假设吃饭的时间),sleep需要在"取餐代码"后面(可理解为取走队列中的菜品后才可以sleep享用)
- 利用synchronized是"谁拿到这个锁谁就可以运行它所控制的那段代码
private static void Consumer() {
while(true) {
try {
synchronized (queue){
while (queue.isEmpty()) {queue.wait();}
//需要使用while来不断循环判断队列中是否有菜, 只用if只会判断一次,队列中有可能会有很多菜品
queue.poll();
String name = Thread.currentThread().getName();//获取线程名字
System.out.println(new Date() + " "+ name+" 取走了餐 正在享用!!!");
}
Thread.sleep(6000);//取走需要享用的等待时间
} catch (Exception e) {
e.printStackTrace();
}
}
}
生产者消费者模式代码(无限循环式)
- 需要自身停止
package ConsumerProduce;//注意自身的包名
import java.util.Date;
import java.util.LinkedList;
import java.util.Queue;
public class ConsumerProduce {
private static Queue<Object> queue = new LinkedList<>();//基于链表创建队列数据结构
public static void main(String[] args) {
new Thread(ConsumerProduce::Producer, "chef.1").start();
new Thread(ConsumerProduce::Producer, "chef.2").start();
new Thread(ConsumerProduce::Consumer, "client.1").start();
new Thread(ConsumerProduce::Consumer, "client.2").start();
}
private static void Producer() {
while (true) {
try {
Thread.sleep(3000);
synchronized (queue){
String name = Thread.currentThread().getName();//获取线程名字
System.out.println(new Date() + " "+ name+" 出餐了!!!");
queue.offer(new Object());
queue.notifyAll();
//唤醒queue.wait的线程, 通知队列客户可以取餐
//因为有好几个客户线程,几个客户线程的队列都可能为空然后被wait,所以需要唤醒几个客户线程的队列
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static void Consumer() {
while(true) {
try {
synchronized (queue){
while (queue.isEmpty()) {queue.wait();}
//需要使用while来不断循环判断队列中是否有菜, 只用if只会判断一次,队列中有可能会有很多菜品
queue.poll();
String name = Thread.currentThread().getName();
System.out.println(new Date() + " " + name +" 取走了餐 正在享用!!!");
}
Thread.sleep(6000);//取走需要享用的等待时间
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
注意
-
需要注意synchronized的使用; 例如上述代码, 谁获取了queue对象的锁谁就可以运行它所控制的那段代码
-
使用的是基于链表创建的队列, 理解为链表样式的队列结构;需要对数据结构队列有一定认识;如offer和poll的操作
代码解析
1: chef.1在14s出餐了(需要3s后(17s)才可出餐)
2: client.2在14s取走了(需要6s后(20s)才可以再取餐)
3: chef.2在14s出餐了(需要3s后(17s)才可出餐)
4: client.1在在14s取走了(需要6s后(20s)才可以再取餐)
5: chef.1在17s出餐了(需要3s后(20s)才可出餐)
6: chef.2在17s出餐了(需要3s后(20s)才可出餐)
7: client.1在在20s取走了(需要6s后(26s)才可以再取餐)
8: client.2在在20s取走了(需要6s后(26s)才可以再取餐)
9: chef.1在20s出餐了(需要3s后(23s)才可出餐)
10: chef.2在20s出餐了(需要3s后(23s)才可出餐)
11: chef.2在23s出餐了(需要3s后(26s)才可出餐)
12: chef.2在23s出餐了(需要3s后(26s)才可出餐)
-
chef.1和chef.2各连续出餐两次,并没有client取餐; 原因正是client正在享用(6s进食)
-
最后两行client.1和client.2都是于26s取走菜品
本文来自博客园,作者:Wo_OD,转载请注明原文链接:https://www.cnblogs.com/WoOD-outPut/p/17139552.html