等待唤醒机制作用
简单来说,等待唤醒机制就是让多线程交替执行
生产者和消费者的理想情况
这里我们以厨师、吃货和桌子举例,吃货代表消费者用来消费数据,厨师代表生产者用来生产数据,利用桌子来控制线程的执行。所谓的理想情况就是厨师做一碗面条放到桌子上吃货来吃,又做了一碗吃货又去桌子上吃,以此交替执行。
生产者和消费者(消费者等待)
所谓消费者等待就是吃货先判断桌子上是否有食物,如果没有食物就等待,然后生产者制作食物,再把食物放到桌子上,最后唤醒等待的消费者来吃。
生产者和消费者(生产者等待)
生产者先判断桌子上是否有食物,如果有就等待,如果没有就去制作食物,然后把食物放到桌子上,最后叫醒消费者开吃。消费者也会先判断桌子上是否有食物,如果没有就等待,如果有就开吃,吃完之后,唤醒厨师继续做。
生产者和消费者常用方法
方法名称 | 说明 |
---|---|
void wait() | 当前线程等待,直到被其他线程唤醒 |
void notify() | 随机唤醒单个线程 |
void notifyAll() | 唤醒全部线程(常用) |
代码演示:
生产者类(厨师)
package com.liming.waitandnotify;
/**
* 生产者(厨师)
*/
public class Cook extends Thread{
@Override
public void run() {
while (true){
synchronized (Desk.lock){
if (Desk.count == 0){
break;
}else {
//1、判断桌子上是否有事物
if (Desk.foodFlag == 1){
//2、如果有就等待
try {
Desk.lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
//3、如果没有,就制作面条
System.out.println("厨师做了一碗面条");
//4、修改桌子状态
Desk.foodFlag = 1;
//5、唤醒等待的消费者来吃
Desk.lock.notifyAll();
}
}
}
}
}
}
消费者类(吃货)
package com.liming.waitandnotify;
/**
* 消费者(吃货)
*/
public class Foodie extends Thread{
@Override
public void run() {
/*
* 多线程的4步套路:
* 1、循环
* 2、同步代码块
* 3、判断共享数据是否到了末尾(到了末尾)
* 4、判断共享数据是否到了末尾(没有到末尾,执行核心逻辑)
* */
while (true){
synchronized (Desk.lock){
//到末尾了
if (Desk.count == 0){
break;
}else {
//没有到末尾执行核心逻辑
//1、先判断桌子上是否有面条
if (Desk.foodFlag == 0){
//2、没有,消费者等待
try {
Desk.lock.wait();//让当前线程跟锁进行绑定
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
//3、把吃的总数-1
Desk.count--;
//4、如果有,开吃
System.out.println("吃货在吃面条还能再吃"+Desk.count+"碗");
//5、吃完之后唤醒厨师继续做
Desk.lock.notifyAll();
//6、修改桌子的状态
Desk.foodFlag = 0;
}
}
}
}
}
}
控制线程(桌子)
package com.liming.waitandnotify;
/**
* 桌子
*/
public class Desk {
/*
* 作用:控制生产者和消费者的执行
* */
//是否有面条 0:有面条 1:没有面条
public static int foodFlag = 0;
//总个数,表示吃货最多吃10碗
public static int count = 10;
//锁对象
public static Object lock = new Object();
}
测试类
package com.liming.waitandnotify;
public class TreadDemo {
public static void main(String[] args) {
/*
* 需求:完成生产者和消费者(等待唤醒机制)的代码
* 实现线程轮流交替执行的效果
* */
//创建线程对象
Cook cook = new Cook();
Foodie foodie = new Foodie();
cook.setName("厨师");
foodie.setName("吃货");
cook.start();
foodie.start();
}
}
运行效果图:
等待唤醒机制(阻塞队列方式实现)
阻塞队列的继承结构
接口:Iterable➡Collection➡Queue➡BlockingQueue
实现类:ArrayBlockingQueue:底层是数组,有界
LinkedBlockingQueue:底层是链表,无界,但不是真正的无界,最大为int的最大值
代码演示:
生产者类(厨师)
package com.liming.waitandnotify01;
import java.util.concurrent.ArrayBlockingQueue;
public class Cook extends Thread{
ArrayBlockingQueue<String> queue;
public Cook(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.liming.waitandnotify01;
import java.util.concurrent.ArrayBlockingQueue;
public class Foodie extends Thread{
ArrayBlockingQueue<String> queue;
public Foodie(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.liming.waitandnotify01;
import java.util.concurrent.ArrayBlockingQueue;
public class ThreadDemo {
public static void main(String[] args) {
/*
* 需求:利用阻塞队列完成生产者和消费者(等待唤醒机制)的代码
* 细节:
* 生产者和消费者必须使用同一个阻塞队列
* 阻塞队列里面是有锁的不需要我们自己添加
* */
//1、创建阻塞队列对象
ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
//2、创建线程的对象,并把阻塞队列传递上去
Cook cook = new Cook(queue);
Foodie foodie = new Foodie(queue);
//3、开启线程
cook.start();
foodie.start();
}
}