多线程——通信( wait和notify)

注意点:
wait(),notify(),notifyAll()方法
(1)在语法上,都必须在synchronized代码块中,如果不在synchronized代码块中,就会抛出异常。
(2)由于在synchronized代码块中,所以当前线程一定是处于运行态的,且成功获取对象锁的。

wait()方法

  • 使当前线程停止运行
  • 当前线程释放持有的对象锁,并由运行态转变为阻塞态(具体为:等待/超时等待,进入等待队列)。直到其他线程调用此对象的notify或者notifyall方法,将当前线程唤醒(由阻塞态转变为就绪态)。
  • wait()方法执行后,当前线程释放锁,线程与其他线程会再次竞争对象锁。

notify()/notifyAll()方法

  • 使被停止线程继续运行
  • notify():随机唤醒一个被wait方法阻塞的线程。
  • notifyAll():唤醒所欲被wait方法阻塞的线程。
  • 该方法会唤醒两个部分的阻塞线程:
    (1)调用wait()释放对象锁的阻塞线程
    (2)在synchronized竞争对象锁失败的线程

notify是将停止的线程唤醒,由于wait使线程处于阻塞态,所以notify会唤醒之前被wait阻塞的线程。又因为notify是在synchronized代码块结束后才唤醒线程,所以类似看起来是之前竞争对象锁失败的线程被notify唤醒,其实是因为synchronized代码块执行完毕,同步队列的线程本身就会被再次唤醒,与notify无关。

  • 在调用notify方法后,当前线程不会马上释放锁
  • 该线程的对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块后,才会释放对象锁。

用面包店的例子来说明什么时候该用wait()/notify()
在这里插入图片描述
代码:

/**
 * 假设面包店有面包师傅生产面包,消费者消费面包
 * 1.面包师傅有5个,可以一直生产面包,每人每次生产3个
 * 2.消费者有20个,可以一直消费面包,每次消费1个
 * 3.面包店库存的上限是100个,到达100个就不再生产,下限是0;
 */
public class SingnalTest {
    //库存
    private static int SUM;

    public static void main(String[] args) {
        //5个面包师傅同时启动
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    while (true) {
                        //加锁保证线程安全,保证可见性 (类加锁)
                        synchronized (SingnalTest.class) {
                            if (SUM + 5 > 100) {
                                SingnalTest.class.wait();
                            } else {
                                SUM += 5;
                                System.out.println(Thread.currentThread().getName()+"生产了面包:库存:"+SUM);
                                Thread.sleep(500);
//                                SingnalTest.class.notify();//随机通知一个wait方法阻塞的线程
                                SingnalTest.class.notifyAll();//通知全部wait方法阻塞的线程
                            }
                        }
                        Thread.sleep(200);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"面包师傅【"+ i +"】").start();
        }

        //20个消费者,同时启动
        for (int i = 0; i < 20; i++) {
            new Thread(() -> {
                try {
                    while (true) {
                        //加锁保证线程安全,保证可见性 (类加锁)
                        synchronized (SingnalTest.class) {
                            if (SUM -3 < 0) {
                                SingnalTest.class.wait();
                            } else {
                                SUM -= 3;
                                System.out.println(Thread.currentThread().getName()+"消费了面包:库存:"+SUM);
                                Thread.sleep(500);
//                                SingnalTest.class.notify();
                                SingnalTest.class.notifyAll();
                            }
                        }
                        Thread.sleep(200);
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            },"消费者【"+ i +"】").start();
        }
    }
}

分析:
在这里插入图片描述在这里插入图片描述在这里插入图片描述
基于消费者代码进行分析:
在这里插入图片描述

wait和sleep方法的对比(面试题)

理论上而言,wait和sleep方法是完全没有可比性的,wait是属于线程间的通信,sleep是将线程阻塞一段时间,相同点就是可以让线程放弃执行一段时间。
面试时,可以回答:

  • wait方法调用之前需要请求锁,(需要在synchronized代码块中)而wait执行时会先释放锁,等再次被唤醒时会重新再次请求锁。
  • sleep方法无视锁的存在,即被调用时,之前请求的锁不会被释放,没有锁也不可以。
  • wait是Object的方法,sleep是Thread的静态方法。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值