【线程基础】【三】wait()、notify()、notifyAll()方法详解
1 前言
本节我们来看下关于线程的一些方法,wait()、notify()、notifyAll(),来理解他们内部做的事情。
2 含义
我们先来看下这三个方法的归属,都是属于Object的方法,因为这些方法都是要跟Synchronized来搭配使用的,就涉及到锁对象,而锁对象可以是任意引用对象,所以归属到了Object里。
wait()
方法是让当前线程等待的,即让线程释放了对共享对象的锁。
notify()
方法或者notifyAll()会让调用了wait()
系列方法的一个线程释放锁,并通知其它正在等待(调用了wait()方法)的线程得到锁。只不过notifyAll()
方法会唤醒所有在锁对象上由于调用wait系列方法而被挂起的线程,其实就是monitor监视器对象有个等待队列里的线程,详细的可以看我之前的Synchronized文章。
注意:
(1)调用wait()、notify()、notifyAll()方法时,当前线程必须要成功获得锁(必须写在同步代码块锁中),否则将抛出异常。
(2)只对当前单个锁对象生效,多个锁对象需要多次调用wait()方法。
(3)如果线程A调用wait()方法后处于阻塞状态时,其他线程中断(在其他线程调用A.interrupt()方法)A线程,则会抛出InterruptExcption异常而返回并终止。
3 源码
接下来我们就来看看三个方法的源码:
3.1 wait 方法
/** * 该方法会抛出两个异常 * InterruptedException 也就是线程wait的时候被调用interrupt方法后,会抛出中断异常 * IllegalMonitorStateException 当前线程未获得锁对象时调用会抛出该异常 * @throws InterruptedException */ public final void wait() throws InterruptedException { // 调用构造方法 wait(0); } public final void wait(long timeout, int nanos) throws InterruptedException { // 等待时间小于0 抛出参数异常 if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } // 纳秒不在范围内也是抛出参数异常 if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } // 纳秒大于0 就给毫秒++ if (nanos > 0) { timeout++; } // 调用重载方法 wait(timeout); } public final native void wait(long timeout) throws InterruptedException;
可以看到 wait有三个重载方法,最后都是调用的一个本地方法,注释很详细了哈,就不做过多解释了哈。
3.2 notify、notifyAll 方法
public final native void notify(); public final native void notifyAll();
可以看到两个方法都是本地方法,区别就是notify会唤醒等待队列中的一个,而notifyAll是唤醒等待队列中的所有,并且也都是会抛出 IllegalMonitorStateException 异常,即当线程锁对象的对象头中的锁记录未持有或者状态异常会抛出该异常。
4 运用示例
那我们来写个打印1212121212来体验下:
public class WaitNotifyTest { // 锁对象 private static final Object lock = new Object(); // 打印 1 的线程 public static class One extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { synchronized (lock) { System.out.print(1); lock.notify(); if (i+1 < 10) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } // 打印 2 的线程 public static class Two extends Thread { @Override public void run() { for (int i = 0; i < 10; i++) { synchronized (lock) { System.out.print(2); lock.notify(); if (i+1 < 10) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } public static void main(String[] args) { One one = new One(); Two two = new Two(); one.start(); two.start(); } }
5 小结
好了,关于wait()、notify()、notifyAll()方法的我们就看的差不多了,可以结合我之前的Synchronized来看,里边会详细讲解锁对象的结构的哈,有理解不对的地方欢迎指正哈。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· Obsidian + DeepSeek:免费 AI 助力你的知识管理,让你的笔记飞起来!
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了