wait() 、notify()、notifyAll() 的详细用法


首先,我们知道,线程的执行顺序是随机的(操作系统随机调度的,抢占式执行),但是有时候,我们并不喜欢这种随机的执行,更喜欢的是它们能够顺序的执行,所以,Java就引入了一种机制,wait() 和 notify() ,它们的 作用就是保证线程执行的顺序;

而前面的文章中也讲过一个方法 join(),也是能影响线程的执行顺序,但是呢,这个join只能控制线程结束的顺序,而我们想要的是,线程不结束,也能按照我们自己规定的顺序去依次执行;

💐wait() 讲解

使用 wait() 时要注意一定要搭配 synchronized 使用,否则的话就会抛出异常

在这里插入图片描述

调用 wait() 时,wait() 会做三件事:

  • 使当前的线程处于阻塞等待
  • 释放当前线程获取到的锁
  • 在其他线程中使用锁对象调用notify时或者使用带参数的wait(带有时间参数,超过时间就会被唤醒)被唤醒后,会再重新尝试获取锁
public class Main {
    public static void main(String[] args) {
        Object locker = new Object();
        Thread thread1 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("调用wait(), 阻塞等待");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("thread1被唤醒");
            }
        });
        thread1.start();
    }
}

在这里插入图片描述

可以看到,当执行到 wait() 这行代码时,就一直处于了阻塞等待,需要在其他线程中使用 notify() 来唤醒,而目前代码中没有其他线程,所以就一直等待;这里也需要解释一下,关于调用 wait() 方法,因为 wait() 方法是Object类中的方法,所以,所有的对象都可以调用;

💐notify() 讲解

notify 作用:唤 醒其它调用了 wait() 导致阻塞等待的线程;

1. wati() 和 notify() 都要在加锁的代码块中使用,并且由锁对象调用

2. 使用 notify() 唤醒某个线程时,只能唤醒和调用 notify() 是同一个锁的线程

比如:thread1 线程中使用 锁对象 locker1 调用了 wait() 方法阻塞等待,那么在其他线程中,也要使用 locker1 来调用 notify() 方法对 thread1 进行唤醒

 public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();
        Thread thread1 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("thread1执行,调用wait,进行阻塞,同时锁被释放");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("thread1执行完");
            }
        });

        Thread thread2 = new Thread(() -> {
            //先进行一个睡眠,可以明显的观察效果
            try {
                Thread.sleep(100);//让thread1先执行
                System.out.println("thread2执行,进入睡眠四秒");
                Thread.sleep(4000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (locker) {
                System.out.println("四秒后");
                System.out.println("进行唤醒");
                locker.notify();
            }
        });
        thread1.start();
        thread2.start();
   
 }

在这里插入图片描述
所以,wait() 结束的条件是:

  1. 其他的线程调用 notify() 方法
  2. 使用带有时间参数的 wait() 方法,等到时间到了以后,就自动结束等待
  3. 其他的线程调用该等待线程的 interrupted 方法,进行线程阻断,导致 wait() 抛出 InterruptedException 异常

wait() 、notify() 也可以避免“线程饿死”

举个例子:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

💐notifyAll()

如果多个线程都调用 wait() 的话,多个线程都会进入阻塞,调用 notify() 的话,就只能唤醒一个,但是用 notifyAll() 的话,就可以一次性全部唤醒(唤醒的是等待 同一个锁的所有线程),这里要注意一点:多个线程被同时唤醒时,只有一个线程可以成功的获取到锁,其他的线程进行继续等待

场景:有四个线程,thread1,thread2,thread3 调用 wait() 进行阻塞等待,thread4 调用 notify(),最终,三个阻塞的线程只会有一个被唤醒,代码如下:

public class Main {
    public static void main(String[] args) {
        Object locker = new Object();
        Thread thread1 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("thread1调用wait(), 阻塞等待");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("thread1被唤醒");
            }
        });
        Thread thread2 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("thread2调用wait(), 阻塞等待");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("thread2被唤醒");
            }
        });
        Thread thread3 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("thread3调用wait(), 阻塞等待");
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("thread3被唤醒");
            }
        });

        Thread thread4 = new Thread(() -> {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (locker) {
                locker.notify();
                System.out.println("thread4调用notify()");
            }
        });

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }
}

执行结果:

在这里插入图片描述

如果将 notify() 换成 调用 notifyAll(),那么就会全部被唤醒,代码如下:

        Thread thread4 = new Thread(() -> {
            try {
                Thread.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            synchronized (locker) {
                locker.notifyAll();
                System.out.println("thread4调用notifyAll()");
            }
        }

通过结果可以看到,当 thread4 调用了 notifyAll() 后, thread3 先获取到了锁,然后释放锁后,thread2 又获取到了锁,最后是 thread1

在这里插入图片描述

💡wait() 和 sleep() 的区别

  • 类不同:sleep() 是Thread线程类的静态方法, wait() 是 Object类的方法
  • 调用后是否释放锁: sleep() 调用后不会有释放锁的操作; wait() 调用后会释放锁
  • 用途不同: wait() 通常用于线程间交互/通信, sleep() 通常用于暂停执行
  • 用法不同:wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用 notify() 方法, 或者 notifyAll() 方法 或者是使用wait(long timeout),指定一个阻塞时间,超时后线程自动苏醒。sleep() 方法执行完后,线程会自动苏醒。
  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
`wait()` 和 `notify()` 方法是 Java 中用于线程间通信的两个重要方法。`wait()` 方法用于让线程进入等待状态,并释放锁,而 `notify()` 方法则用于唤醒等待状态的线程。这两个方法必须在同步方法或同步块中使用,否则会抛出 `IllegalMonitorStateException` 异常。 `wait()` 方法的调用会让线程进入等待状态,直到有其他线程调用了同一个对象的 `notify()` 或 `notifyAll()` 方法来唤醒它。在等待期间,线程会释放它持有的锁,以便其他线程可以进入同步块执行。`wait()` 方法可以使用以下两种方式调用: - `wait()`:让线程一直等待,直到其他线程调用了 `notify()` 或 `notifyAll()` 方法唤醒它。 - `wait(long timeout)`:让线程等待一段时间,如果在等待期间没有其他线程调用 `notify()` 或 `notifyAll()` 方法唤醒它,那么线程会自动醒来。 `notify()` 方法用于唤醒等待状态的线程。它会随机地唤醒一个等待状态的线程,如果有多个线程都在等待同一个对象的锁,那么只有其中一个线程会被唤醒。`notifyAll()` 方法则会唤醒所有等待状态的线程。 以下是一个简单的示例,展示了如何使用 `wait()` 和 `notify()` 方法进行线程间通信: ```java class MyThread implements Runnable { private final Object lock; public MyThread(Object lock) { this.lock = lock; } @Override public void run() { synchronized (lock) { System.out.println("Thread " + Thread.currentThread().getName() + " is waiting"); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread " + Thread.currentThread().getName() + " is awake"); } } } public class Main { public static void main(String[] args) throws InterruptedException { Object lock = new Object(); Thread t1 = new Thread(new MyThread(lock)); Thread t2 = new Thread(new MyThread(lock)); t1.start(); t2.start(); Thread.sleep(1000); synchronized (lock) { lock.notify(); } } } ``` 在这个示例中,我们创建了两个线程 `t1` 和 `t2`,它们都在同一个对象 `lock` 上等待。在主线程中,我们等待 1 秒钟后调用了 `notify()` 方法来唤醒一个等待状态的线程。由于 `notify()` 方法是随机唤醒一个线程,因此我们无法确定哪个线程会被唤醒。在这个示例中,我们可以看到其中一个线程被唤醒并输出了 "Thread X is awake" 的信息。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值