🍊一. 为什么需要线程通信
线程是并发并行的执行,表现出来是线程随机执行,但是我们在实际应用中对线程的执行顺序是有要求的,这就需要用到线程通信
🍖线程通信为什么不使用优先级来来解决线程的运行顺序?
总的优先级是由线程pcb中的优先级信息和线程等待时间共同决定的,所以一般开发中不会依赖优先级来表示线程的执行顺序
🍖看下面这样的一个场景:面包房的例子来描述生产者消费者模型
有一个面包房,里面有面包师傅和顾客,对应我们的生产者和消费者,而面包房有一个库存用来存储面包,当库存满了之后就不在生产,同时消费者也在购买面包,当库存面包卖完了之后,消费者必须等待新的面包生产出来才能继续购买
分析: 对于何时停止生产何时停止消费就需要应用到线程通信来准确的传达生产和消费信息
🍉二. wait和notify方法
🍃wait():让当前线程持有的对象锁释放并等待
🍃wait(long timeout):对应的参数是线程等待的时间
🍃notify():唤醒使用同一个对象调用wait进入等待的线程,重新竞争对象锁
🍃notifyAll():如果有多个线程等待,notifyAll是全部唤醒 ,notify是随机唤醒一个
👁🗨️注意:
🍂这几个方法都属于Object类中的方法
🍂必须使用在synchronized同步代码块/同步方法中
🍂哪个对象加锁,就是用哪个对象wait,notify
🍂调用notify后不是立即唤醒,而是等synchronized结束以后,才唤醒
🌴1. wait()方法
🍖调用wait方法后:
🍁使执行当前代码的线程进行等待(线程放在等待队列)
🍁释放当前的锁
🍁满足一定条件时被唤醒,重新尝试获取锁
🍖wait等待结束的条件:
🍃其他线程调用该对象的notify方法
🍃wait等待时间超时(timeout参数来指定等待时间)
🍃其他线程调用interrupted方法,导致wait抛出InterruptedException异常
🌾2. notify()方法
当使用wait不带参数的方法时,唤醒线程等待就需要使用notify方法
🍀这个方法是唤醒那些等待该对象的对象锁的线程,使他们可以重新获取该对象的对象锁
🍀如果有多个线程等待,则由线程调度器随机挑选出一个呈wait 状态的线程(不存在先来后到)
🍀在notify()方法后,当前线程不会马上释放该对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块之后才会释放对象锁
🌵3. notifyAll()方法
该方法和notify()方法作用一样,只是唤醒的时候,将所有等待的线程都唤醒
notify()方法只是随机唤醒一个线程
🍏三. 使用wait和notify实现面包房业务
前提说明:
有2个面包师傅,面包师傅一次可以做出两个面包
仓库可以存储100个面包
有10个消费者,每个消费者一次购买一个面包
👁🗨️注意:
消费和生产是同时并发并行进行的,不是一次生产一次消费
👁🗨️实现代码:
public class Bakery {
private static int total;//库存
public static void main(String[] args) {
Producer producer = new Producer();
for(int i = 0;i < 2;i++){
new Thread(producer,"面包师傅-"+(i-1)).start();
}
Consumer consumer = new Consumer();
for(int i = 0;i 100){ //仓库满了,生产者等待
Bakery.class.wait();
}
//等待解除
total += num;
System.out.println(Thread.currentThread().getName()+"生产面包,库存:"+total);
Thread.sleep(500);
Bakery.class.notifyAll(); //唤醒生产
}
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private static class Consumer implements Runnable{
private int num = 1; //消费者每次消费1个面包
@Override
public void run() {
try {
while(true){ //一直消费
synchronized (Bakery.class){
while((total-num)