多线程编程7——wait和notify、notifyAll

线程最大的问题就是抢占式执行,随机调度。可以通过一些API让线程主动阻塞,主动放弃CPU,从而控制线程之间的执行顺序。比如:join,sleep,wait和notify、notifyAll

前面章节已经介绍过 join 和 sleep了,那么接下来我们来介绍介绍wait 和 notify、notifyAll

wait 和 notify、notifyAll 是 Object 类中的方法。Java 里任意一个对象都有这两个方法。

1、wait()

正确使用 wait 的代码如下(搭配 synchronized 使用):

public class ThreadDemo15 {
    public static void main(String[] args) {
        Object object = new Object();
        //这个线程负责等待
        Thread t1 = new Thread(()->{
            System.out.println("wait 之前");
            try {
                synchronized (object){
                    object.wait();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("wait 之后");
        });
        t1.start();
    }
}

在线程中调用wait,线程进入阻塞等待状态,比如在 线程t1 中调用 wait,就是让线程t1 进入阻塞等待状态(WAITING)。有有参和无参两个版本。无参版本,就是死等,直到被通知唤醒。有参版本,就是规定了一个最大的等待时间,超过时间就不等了。

wait 操作有3步:1、先释放2、进行阻塞等待 3、收到通知(notify)之后,重新尝试获取锁,并在获取锁后,继续往下执行

所以,wait 要搭配 synchronizwd 来使用,要先获取到锁,才能执行 wait 释放锁,否则就会报异常(IllegalMonitorStateException,非法的锁状态异常)。就像,你还没有女朋友,是个单身狗呢,就想分手,你要和谁分啊。

无论在线程中是通过哪个锁对象 wait 的,这个线程都会处于阻塞等待状态,且阻塞在 synchronized 代码块内,此时是释放了锁的,其他线程是可以对 锁对象 加锁的。

wait 结束等待的条件如下:

1、如果 wait 有参数,超过时间就不等了

2、interrupt 触发异常唤醒 wait

3、其他线程调用了该对象的notify方法(这个对象就是锁对象),通知在 同一个对象上 等待的线程,此时在同一个对象上等待的线程就会重新尝试加锁。(只能有一个线程会加锁成功)

报 非法的锁状态异常 的代码如下:

public class ThreadDemo15 {
    public static void main(String[] args) {
        Object object = new Object();
        //这个线程负责等待
        Thread t1 = new Thread(()->{
            System.out.println("wait 之前");
            try {
               object.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("wait 之后");
        });
        t1.start();
    }
}

没有加锁,就想执行 wait 释放锁,报异常。

2、notify()

通知在同一个对象上等待的线程也就是说,此处 notify 的对象要和 wait 的对象要相同,如果通知和等待是两个不同对象,通知就不会生效,wait就不会唤醒。

也要结合 synchronized关键字,要先获取到锁,才能通知。

public class ThreadDemo15 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();
        //这个线程负责等待
        Thread t1 = new Thread(()->{
            System.out.println("wait 之前");
            try {
                synchronized (object){
                    object.wait();
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("wait 之后");
        });
       //这个线程负责通知
        Thread t2 = new Thread(()->{
            System.out.println("notify之前");
            synchronized (object){
                object.notify();
            }
            System.out.println("notify之后");
        });
        t1.start();
        Thread.sleep(500);
        t2.start();
    }
}

四个地方的对象都要相同,比如上面,都是object。wait使用的对象要和notify使用的对象相同,不然就通知了个寂寞,notify不会有任何效果。notify 只能唤醒在同一个对象上等待的线程。

3、总结

1、wait和notify都要先获取到锁才能使用。获取到锁后,使用wait,线程会先释放锁对象,然后阻塞等待;使用notify,会通知在同一个锁对象上等待的线程,如果当前有多个线程在等待同一个锁对象,会随机唤醒一个等待的线程;而notifyAll,是所有线程都唤醒,这些线程再一起竞争锁。

2、wait和sleep的区别:

(1)虽然wait和sleep都能被提前唤醒,wait是使用notify唤醒,sleep是使用interrupt唤醒。但是notify唤醒wait,不会有任何异常,是正常的业务逻辑。而interrupt唤醒sleep,则是出现了异常,表示一个出问题的逻辑。

(2)wait需要搭配synchronized使用,sleep不需要

(3)wait是Object的方法,sleep是Thread的方法

3、wait和join的区别:

wait可以使用notify提前唤醒,但是join则必须得等这个线程彻底执行完,下个线程才能执行,不能提前唤醒。

4、wait和notify的使用 

wait和notify可以对多个线程的执行顺序进行控制。wait会让调用的线程进行阻塞,通过其他线程的notify进行通知。

如:三个线程,分别只能打印A,B,C,请你写个代码,保证这三个线程,固定按照ABC这样的顺序来打印

 public static void main(String[] args) throws InterruptedException {
        //让 C 后于 B 打印,让 B后于 A打印
        Object object1 = new Object();
        Object object2 = new Object();

        Thread t1 = new Thread(()->{
            System.out.println("A");
            synchronized (object2) {
                object2.notify();
            }
        });
        Thread t2 = new Thread(()->{
            synchronized (object2){
                try {
                    object2.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("B");
            synchronized (object1){
                object1.notify();
            }
        });
        Thread t3 = new Thread(()->{
            synchronized (object1){
                try {
                    object1.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
            System.out.println("C");
        });
        t2.start();
        t3.start();
        //防止notify通知了个寂寞,wait没人唤醒了
        Thread.sleep(1000);
        t1.start();
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值