理解wait和notify


前言

线程最大的问题是抢占式执行,随机调度,但是随机性的东西,往往是最难掌握的,为了解决这个问题,从而会使用一些API让线程主动阻塞,主动放弃CPU,而wait和notify就是其中之一。


一、场景描述

下面出现一个场景:t1和t2两个线程,希望t1先来执行,当t1的线程差不多执行到一半时,就让t1等待,通知t2开始执行,等t2完成执行后,再通知t1继续执行。

上面的场景使用sleep和join行不行?

1.使用sleep,需要指定一个具体休眠时间,在上面的场景中,我们并不知道t1执行需要多少时间,所以使用sleep是不行的;
2.使用join,需要t1彻底执行完成后,才可以让t2开始执行,不满足上述场景。

我们可以使用wait和notify来解决上述场景

二、wait和notify

1.wait

1.wait进行阻塞

某个线程调用wait方法,就会进入阻塞状态,进入WAITING

2.wait的使用

1.throws InterruptedException: 这个异常,大多数带有阻塞功能的方法都带有,这些方法都可以被interrupt方法通过这个异常给唤醒

2.object.wait():wait不参加任何参数,即为死等,一直等待其他线程唤醒它

在这里插入图片描述

注意: 上面的代码是会报错的

在这里插入图片描述

1.IllegalMonitorStateException:非法的锁状态异常,通常情况下,锁的状态只有两种,加锁和不加锁。

2.然后我们再来理解一下wait的操作

1.先释放锁
2.进行阻塞等待
3.收到通知后,重新尝试获取锁,并且在获取锁后,继续向下执行

很显然在上面的代码中,我们可以看出上面处于不加锁状态,不满足第一个条件,现在连锁都没有,哪里还需要释放锁,这就好比,你还单身就想着要分手,还是先找个对象再说吧,因此wait操作需要搭配synchronized来一起使用


public class test2 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        synchronized (object) {
            System.out.println("wait 之前");
            object.wait();
            System.out.println("wait 之后");
        }
    }

代码结果:

在这里插入图片描述

1.object.wait():虽然这里的wait是阻塞,阻塞在synchronized的代码块当中,但是实际上已经释放锁,其他线程是可以获取这个object对象的

2.上面的代码中,我们会发现一个问题,因为没有notify,所以上面的在打印“wait之前"后,该线程会一直死等下去,这样显然不是一个很好的结果,所以wait会有带参数版本和没有参数版本,wait有参数版本,指定了等待的最大时间,这和sleep有点相似,但是是有本质差别

虽然两者都能等待时间,虽然都可以被提前唤醒(wait可以使用notify唤醒,sleep可以使用interrupt唤醒),但是这两种唤醒的含义是不一样的,notify唤醒wait,是不会有异常的,但是interrupt唤醒sleep则表示出现了异常

2.notify

1.notify唤醒线程

notify 方法是唤醒等待的线程

2.notify的使用

示例代码:

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

        Thread t1 = new Thread(() -> {
            // 这个线程负责进行等待
            System.out.println("t1: wait 之前");
            try {
                synchronized (object) {
                    object.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1: wait 之后");
        });

        Thread t2 = new Thread(() -> {
            System.out.println("t2: notify 之前");
            synchronized (object) {
                // notify 务必要获取到锁, 才能进行通知
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                object.notify();
            }
            System.out.println("t2: notify 之后");
        });

        t1.start();
        Thread.sleep(500);
        t2.start();
    }
}

运行结果

在这里插入图片描述

1.在上面的示例的代码中,下面后框的四个对象都相同,才能够正确生效,这里的notify()和wait()进行配对,使用相同对象,如果不同就不会生效

在这里插入图片描述

2.Thread.sleep(500) 是因为如果不添加这个,只有t1.start()和t2.start(),由于进程调度的随机性,不一定保证先执行wait,然后执行notify,如果此时调用notify,没有wait,那么此处的wait就无法被唤醒,但是如果你的电脑比较卡,有可能sleep(500)可能还不行,需要更长时间。

3.notifyAll()的使用

上面notify方法只是唤醒某一个等待线程,这个显然是不够用的,使用notifyAll方法可以一次唤醒所有的等待线程

注意: 虽然是同时唤醒 3 个线程, 但是这 3 个线程需要竞争锁。 所以并不是同时执行, 而仍然是有先有后的执行

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

        Thread t1 = new Thread(() -> {
            // 这个线程负责进行等待
            System.out.println("t1: wait 之前");
            try {
                synchronized (object) {
                    object.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t1: wait 之后");
        });
        Thread t2 = new Thread(() -> {
            // 这个线程负责进行等待
            System.out.println("t2: wait 之前");
            try {
                synchronized (object) {
                    object.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2: wait 之后");
        });
        Thread t3 = new Thread(() -> {
            // 这个线程负责进行等待
            System.out.println("t3: wait 之前");
            try {
                synchronized (object) {
                    object.wait();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t3: wait 之后");
        });
        Thread t4 = new Thread(() -> {
            System.out.println("t4: notifyAll 之前");
            synchronized (object) {
                // notify 务必要获取到锁, 才能进行通知
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                object.notifyAll();
            }
            System.out.println("t4: notifyAll 之后");
        });

        t1.start();
        t2.start();
        t3.start();
        Thread.sleep(500);
        t4.start();
    }
}

代码结果

在这里插入图片描述


总结

在Java中,wait和notify是两个用于线程间通信的方法,它们通常与synchronized关键字一起使用,需要注意的是,wait、notify和notifyAll方法必须在同步代码块或同步方法中调用,否则会抛出IllegalMonitorStateException异常。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值