线程安全3--wait和notify

wait and notify(等待通知机制

引入wait notify就是为了能够从应用层面上,干预到多个不同线程代码的执行顺序,这里说的干预,不是影响系统的线程调度策略(内核里调度线程,仍然是无序调度)
相当于是在应用程序代码中,让后执行的线程,主动放弃被调度的机会,就可以让先执行的线程,先把对应代码执行完了
举例:
在ATM机上取钱
在这里插入图片描述
ATM通过锁来互斥
第一个人,先进去,发现ATM没钱,然后出来了,但是和其他人来竞争这个ATM,参与到了锁竞争中,此时完全有可能第一个人再次拿到这个锁,如果反复如此,就会导致第一个人反复获取到锁,但又无法完成实质性的逻辑,导致“线程饿死”。
这种情况就是严重的bug,当第一个人发现自己要执行的逻辑,前提条件不具备,在这种情况下,应该主动放弃对锁的竞争,一直到条件具备,此时再解除阻塞,参与锁竞争,这个时候就要用到wait和notify
在这里插入图片描述
join是等待另一个线程执行完,才继续执行
wait则是等待另一个线程通过notify进行通知(不要求另一个线程必须执行完)
wait进入阻塞,只能说明自己释放锁了
其他线程是否拿到了锁,另当别论

阻塞产生的原因有好几种:
1.sleep TIMED_WAITING
2.join/wait WAITING
3.synchronized BLOCKED

Object object = new Object();
        object.wait();

随便拿一个对象都能调用wait但是运行起来会报异常
在这里插入图片描述
不合法监视状态异常
原因:wait一旦调用就会释放锁,释放锁的前提是拿到锁,所以,wait必须放到synchronized里面使用。
wait锁对象必须和synchronized锁对象一致

public class Test4 {
    public static void main(String[] args) {
        Object object = new Object();
        Thread t1 = new Thread(()->{
            synchronized (object){
                System.out.println("wait前");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait后");
            }
        });
        t1.start();
    }
}

输出结果:

wait前

代码一直没有结束

在Java监视和管理控制台上:
在这里插入图片描述

notify

锁被wait就需要来唤醒锁,notify就是来唤醒锁的
notify可以不放在synchronized里面,但是Java规定notify必须放在synchronized里面

public class Test4 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();

      Thread t1 = new Thread(()->{
            synchronized (object){
                System.out.println("wait前");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait后");
            }
        });
      Thread t2 = new Thread(()->{
          try {
              Thread.sleep(2000);
          } catch (InterruptedException e) {
              throw new RuntimeException(e);
          }
          synchronized(object){
              System.out.println("notify前");
              object.notify();
              System.out.println("notify后");
          }
      });

        t1.start();
        t2.start();
    }
}

运行结果:

wait前
notify前
notify后
wait后

线程2使用sleep的原因
因为线程调度是随机的,不确定线程1还是线程2先运行,如果线程2先运行就达不到我们预期的结果,所以sleep线程2,让线程1先运行,先执行wait,再执行notify。

上述代码运行执行过程:
1.t1先执行起来后,就会立即拿到锁,并打印wait前,然后进入wait方法(释放锁+阻塞等待)
2.t2执行起来,先进行sleep五秒(为了让t1先拿到锁)
3.t2sleep结束后,由于t1是wait状态,锁是释放的,t2就能拿到锁,接下来打印notify前,执行notify操作,唤醒t1
4.由于t2还没有释放锁,t1想要获取锁,可能会出现阻塞,和t2锁竞争导致
5.t2打印notify后,释放锁,t2执行完毕,t1获取到锁,打印wait后

补充

在这里插入图片描述
在这里插入图片描述

  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mang go

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值