Object 中的 wait() 与 notify() 方法有什么用?

3551 篇文章 118 订阅

1.wait()方法和notify()方法

  wait()方法和notify()方法是用于线程间的通信和同步的方法。由于线程之间是抢占式执行的,因此线程之间执行的先后顺序难以预知。但是有时候我们希望合理的协调多个线程之间的执行顺序。这时候就需要用到 Object 中的 wait()方法和notify()方法了。

1.1 wait() 方法

  如果一个线程调用了wait()方法,那么它会经历以下几个阶段:

  1. 线程进入等待状态:调用wait()方法会导致当前线程进入等待状态,暂时停止执行。
  2. 释放对象锁:在调用wait()方法之后,线程会释放它所持有的对象锁。这样,其他线程就有机会获取该对象锁,并执行相应的操作。
  3. 等待其他线程的通知:一旦线程进入等待状态,它会等待其他线程调用相同对象上的notify()方法或notifyAll()方法来通知它恢复执行。线程会保持等待状态,直到被通知。

  补充:可选的等待时间:wait()方法还可以传入一个可选的等待时间参数,用于限制线程等待的时间长度。如果超过指定的等待时间,线程会自动被唤醒。

  我们来看看下面的案例:

java复制代码public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();

        Thread t1 = new Thread(()->{
            System.out.println("wait()方法之前");
            try {
                //要处理 wait() 的异常
                object.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("wait()方法之后");
        });
        t1.start();
    }
}

结果:

  结果抛出异常了,为什么呢?原因是:wait()方法必须要和synchronized搭配使用!既然wait()是暂停执行并放弃锁,你连锁都没有何谈放弃呢?

java复制代码public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();

        Thread t1 = new Thread(()->{
            //wait() 必须要和 synchronized 搭配使用
            synchronized (object){
                System.out.println("wait()方法之前");
                try {
                    object.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait()方法之后");
            }
        });
        t1.start();
    }
}

结果:

  这时 t1 线程已经阻塞,等待着有缘人使用notify()把它唤醒。如果它不想等待有缘人,这里也可以使用定时的wait(timeout)方法。

java复制代码public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();

        Thread t1 = new Thread(()->{

            synchronized (object){
                System.out.println("wait()方法之前");
                try {
                    //1000毫秒后唤醒
                    object.wait(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("wait()方法之后");
            }
        });
        t1.start();
    }
}

结果:

  wait 结束等待的条件:

  1. 其他线程调用该对象的notify方法。
  2. wait 等待时间超时,wait 方法提供一个带有 timeout 参数的版本, 来指定等待时间。
  3. 其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常。

1.2 notify() 方法

  只有wait()可不行呀,不然一直阻塞下去不是办法。所以我们需要使用notify()方法。

  对于notify()方法:

  1. 唤醒等待的线程:调用notify()方法会唤醒在相同对象上(锁)等待的一个线程。如果有多个线程在等待,那么具体唤醒哪个线程是不确定的,取决于操作系统的调度。
  2. 通知等待线程可以继续执行:一旦线程被唤醒,它会从wait()方法的阻塞中恢复过来,并且可以继续执行后续的操作。
  3. 不释放对象锁:与wait()方法不同,notify()方法并不会释放对象的锁。唤醒的线程需要等待调用notify()方法的线程释放对象锁后才能继续执行。
java复制代码public class Demo1 {
    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) {
                throw new RuntimeException(e);
            }
            System.out.println("t1: wait()方法之后");
        });

        Thread t2 = new Thread(()->{
            System.out.println("t2: notify()方法之前");
            //notify() 也要与 synchronized 配合使用
            synchronized (object){
                object.notify();
            }
            System.out.println("t2: notify()方法之后");
        });
        t1.start();
        //这里的 sleep 是确保先让 t1 阻塞
        Thread.sleep(500);
        t2.start();
    }
}

1.3 notifyAll() 方法

  对于notify()方法,如果有多个线程在等待,那么具体唤醒哪个线程是不确定的 。而 notifyAll()方法是唤醒所有等待的线程(同一个锁),然后让这些线程继续争夺锁。

  案例:有三个线程,分别只能打印A、B、C。现在要求控制这三个线程分别进行打印,顺序必须为 A、B、C。

java复制代码class Demo4{

    //记录当前该打印的字母
    static char state = 'A';

    public static void main(String[] args) {

        Object lock = new Object();

        Thread t1 = new Thread(()->{
            synchronized (lock){
                while(state != 'A'){
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                System.out.print("A");
                //A 已经打印了,该打印 B
                state = 'B';
                //这里需要唤醒所有的阻塞线程,然后它们再判断自己该不该打印
                lock.notifyAll();
            }
        });

        Thread t2 = new Thread(()->{
            synchronized (lock){
                while(state != 'B'){
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                System.out.print("B");
                //B 已经打印了,该打印 C
                state = 'C';
                //这里需要唤醒所有的阻塞线程,然后它们再判断自己该不该打印
                lock.notifyAll();
            }
        });

        Thread t3 = new Thread(()->{
            synchronized (lock){
                while(state != 'C'){
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                System.out.print("C");
            }
        });

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

结果:

1.4 wait() 和 sleep() 的对比

相似之处

  • wait和sleep都可以让当前线程进入非可运行状态。
  • wait和sleep都可以指定暂停的时间,单位是毫秒。
  • wait和sleep都可以被其他线程用interrupt()中断,抛出InterruptedException异常。

区别

  • wait是Object类的实例方法,而sleep是Thread类的静态方法。
  • wait必须在同步块或同步方法中调用,而sleep可以在任何地方调用。
  • wait 会释放当前对象的锁,让其他线程可以获取该对象的锁,而sleep不会释放任何锁。
  • 在中途唤醒的时候,wait可以不用抛出异常(调用notify或notifyAll);而sleep会抛出异常(调用interrupt())。
  • wait通常用于线程间的通信,而sleep通常用于控制线程的执行时间。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值