生产者和消费者(等待唤醒机制)
等待唤醒机制
生产者和消费者是一个十分经典的多线程协作模式
举个小栗子来说明一下消费者和生产者的等待唤醒过程:
常见方法
- void wait() 当前线程等待,直到被其他线程唤醒
- void notify() 随机唤醒单个线程
- void notifyAll() 唤醒所有线程
代码演示
接下来,使用代码来演示生产者和消费者的等待唤醒过程
1、消费者代码:
package com.heima.thread001;public class FoodThread extends Thread { @Override public void run() { while (true){ synchronized (Desk.lock){ if (Desk.count == 0){ break; }else { //判断桌子上有没有面条 if (Desk.foodFlag == 0){ //如果没有,就等待 try {Desk.lock.wait();//让当前线程跟锁进行绑定 } catch (InterruptedException e) {e.printStackTrace(); } }else { //把吃的总数-1 Desk.count--; //如果有,就开吃 System.out.println("吃货在吃面条,还能再吃" + Desk.count + "碗"); //吃完之后,唤醒厨师继续做 Desk.lock.notifyAll(); //修改桌子的状态 Desk.foodFlag = 0; } } } } }}
2、生产者代码
package com.heima.thread001;public class CookThread extends Thread { @Override public void run() { while (true){ synchronized (Desk.lock){ //判断桌子上有没有面条 if (Desk.count == 0){ break; }else { //判断桌子上是否有实物 if(Desk.foodFlag == 1){ //如果有,就等待 try {Desk.lock.wait(); } catch (InterruptedException e) {e.printStackTrace(); } }else { //如果没有,就制作面条 System.out.println("厨师做了一碗面条"); //修改桌子上的食物状态 Desk.foodFlag = 1; //叫醒等待的消费者开吃 Desk.lock.notifyAll(); } } } } }}
3、控制生产者和消费者的执行类
package com.heima.thread001;public class Desk { //是否有面条 0:没有面条 1:有面条 public static int foodFlag = 0; //总个数 public static int count = 10; //锁对象 public static Object lock = new Object();}
4、测试类
package com.heima.thread001;public class TestDemo { public static void main(String[] args){ CookThread cookThread = new CookThread(); FoodThread foodThread = new FoodThread(); cookThread.setName("厨师"); foodThread.setName("吃货"); cookThread.start(); foodThread.start(); }}
5、运行结果
Connected to the target VM, address: '127.0.0.1:52025', transport: 'socket'Disconnected from the target VM, address: '127.0.0.1:52025', transport: 'socket'厨师做了一碗面条吃货在吃面条,还能再吃9碗厨师做了一碗面条吃货在吃面条,还能再吃8碗厨师做了一碗面条吃货在吃面条,还能再吃7碗厨师做了一碗面条吃货在吃面条,还能再吃6碗厨师做了一碗面条吃货在吃面条,还能再吃5碗厨师做了一碗面条吃货在吃面条,还能再吃4碗厨师做了一碗面条吃货在吃面条,还能再吃3碗厨师做了一碗面条吃货在吃面条,还能再吃2碗厨师做了一碗面条吃货在吃面条,还能再吃1碗厨师做了一碗面条吃货在吃面条,还能再吃0碗Process finished with exit code 0
等待唤醒机制(阻塞队列方式实现)
阻塞队列的继承结构
代码演示:
生产者代码
package com.heima.thread001;import java.util.concurrent.ArrayBlockingQueue;public class CookThread extends Thread { ArrayBlockingQueue<String> queue; public CookThread(ArrayBlockingQueue<String> queue){ this.queue = queue; } @Override public void run() { while (true){ //不断把面条放到阻塞队列中 try { queue.put("面条"); System.out.println("厨师放了一碗面条"); } catch (InterruptedException e) { e.printStackTrace(); } } }}
消费者
package com.heima.thread001;import java.util.concurrent.ArrayBlockingQueue;public class FoodThread extends Thread { ArrayBlockingQueue<String> queue; public FoodThread(ArrayBlockingQueue<String> queue){ this.queue = queue; } @Override public void run() { while (true){ //不断的从阻塞队列中获取面条 try { String food = queue.take(); System.out.println(food); } catch (InterruptedException e) { e.printStackTrace(); } } }}
测试类
package com.heima.thread001;import java.util.concurrent.ArrayBlockingQueue;public class TestDemo { public static void main(String[] args){ ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1); CookThread c = new CookThread(queue); FoodThread f = new FoodThread(queue); c.start(); f.start(); }}
额外扩展
wait()、notify()、notifyAll()方法
Object类里面提供了这几个方法:
wait():让当前线程处于等待(阻塞状态),直到其他线程调用此对象的notify()或notifyAll()方法(进入就绪状态)。
notify():唤醒在此对象监视器上等待的单个线程。
notifyAll():唤醒在此对象监视器上等待的所有线程。
每个方法都有finnal关键字修饰。
为什么这些方法要定义在Object类里,而不定义在Thread类里呢?
因为这些方法的调用必须通过锁对象调用,而锁对象可以是任意对象。所以定义在Object类里。
来源地址:https://blog.csdn.net/weixin_43860634/article/details/133611415