千家信息网

Java线程通信中wait-notify通信的方式是什么

发表于:2026-11-04 作者:千家信息网编辑
千家信息网最后更新 2026年11月04日,这篇文章主要介绍"Java线程通信中wait-notify通信的方式是什么"的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇"Java线程通信中wait-notif
千家信息网最后更新 2026年11月04日Java线程通信中wait-notify通信的方式是什么

这篇文章主要介绍"Java线程通信中wait-notify通信的方式是什么"的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇"Java线程通信中wait-notify通信的方式是什么"文章能帮助大家解决问题。

    1. 线程通信的定义

    线程是操作系统调度的最小单位,有自己的栈空间,可以按照既定的代码逐步执行,但是如果每个线程间都孤立地运行,就会造资源浪费。所以在现实中,如果需要多个线程按照指定的规则共同完成一个任务,那么这些线程之间就需要互相协调,这个过程被称为线程的通信。

    线程的通信可以被定义为:当多个线程共同操作共享的资源时,线程间通过某种方式互相告知自己的状态,以避免无效的资源争夺。

    线程间通信的方式可以有很多种:等待-通知、共享内存、管道流。"等待-通知"通信方式是Java中使用普遍的线程间通信方式,其经典的案例是"生产者-消费者"模式。

    2. 为什么需要wait-notify?

    场景:有几个小孩都想进入房间内使用算盘(CPU)进行计算,老王(操作系统)就使用了一把锁(synchronized)让同一时间只有一个小孩能进入房间使用算盘,于是他们排队进入房间。

    (1) 小南最先获取到了锁,进入到房间内,但是由于条件不满足(没烟干不了活),小南不能继续进行计算 ,但小南如果一直占用着锁,其它人就得一直阻塞,效率太低。

    (2) 于是老王单开了一间休息室(调用 wait 方法),让小南到休息室(WaitSet)等着去了,这时锁释放开, 其它人可以由老王随机安排进屋

    (3) 直到小M将烟送来,大叫一声 [ 你的烟到了 ] (调用 notify 方法)

    (4) 小南于是可以离开休息室,重新进入竞争锁的队列

    java语言中"等待-通知"方式的线程间通信使用对象的wait()、notify()两类方法来实现。每个java对象都有wait()、notify()两类实例方法,并且wait()、notify()方法和对象的监视器是紧密相关的。

    wait()、notify()两类方法在数量上不止两个。wait()、notify()两类方法不属于Thread类,而是属于java对象实例。

    3. wait方法和notify方法

    java对象中的wait()、notify()两类方法就如同信号开关,用于等待方和通知方之间的交互。

    1、对象的wait()方法

    对象的wait()方法的主要作用是让当前线程阻塞并等待被唤醒。wait()方法与对象监视器紧密相关,使用wait()方法时一定要放在同步块中。wait()方法的调用方法如下:

    public class Main {    static final Object lock = new Object();    public static void method1() throws InterruptedException {        synchronized( lock ) {            lock.wait();        }    }}

    Object类中的wait()方法有三个版本:

    (1) void wait():当前线程调用了同步对象lock的wait()实例方法后,将导致当前的线程等待,当前线程进入lock的监视器WaitSet,等待被其他线程唤醒;

    (2) void wait(long timeout):限时等待。导致当前的线程等待,等待被其他线程唤醒,或者指定的时间timeout用完,线程不再等待;

    (3) void wait(long timeout,int nanos):高精度限时等待,其主要作用是更精确地控制等待时间。参数nanos是一个附加的纳秒级别的等待时间;

    2、对象的notify()方法

    对象的notify()方法的主要作用是唤醒在等待的线程。notify()方法与对象监视器紧密相关,调用notify()方法时也需要放在同步块中。notify()方法的调用方法如下:

    public class Main {    static final Object lock = new Object();    public static void method1() throws InterruptedException {        synchronized( lock ) {            lock.notify();        }    }}

    notify()方法有两个版本:

    (1)void notify():lock.notify()调用后,唤醒lock监视器等待集中的第一条等待线程;被唤醒的线程进入EntryList,其状态从WAITING变成BLOCKED。

    (2) void notifyAll():lock.notifyAll()被调用后,唤醒lock监视器等待集中的全部等待线程,所有被唤醒的线程进入EntryList,线程状态从WAITING变成BLOCKED。

    小结:

    obj.wait():让进入Object监视器的线程到waitset等待

    obj.notify():在Object上正在waitset等待的线程中挑一个唤醒

    obj.notifyAll():让在Object上正在waitset等待的线程全部唤醒

    4. wait方法和notify方法的原理

    对象的wait()方法的核心原理大致如下:

    (1) 当线程调用了lock(某个同步锁对象)的wait()方法后,jvm会将当前线程加入lock监视器的WaitSet(等待集),等待被其他线程唤醒。

    (2) 当前线程会释放lock对象监视器的Owner权利,让其他线程可以抢夺lock对象的监视器。

    (3) 让当前线程等待,其状态变成WAITING。在线程调用了同步对象lock的wait()方法之后,同步对象lock的监视器内部状态大致如图2-15所示。

    对象的notify()或者notifyAll()方法的原理大致如下:

    (1) 当线程调用了lock(某个同步锁对象)的notify()方法后,jvm会唤醒lock监视器WaitSet中的第一条等待线程。

    (2) 当线程调用了lock的notifyAll()方法后,jvm会唤醒lock监视器WaitSet中的所有等待线程。

    (3) 等待线程被唤醒后,会从监视器的WaitSet移动到EntryList,线程具备了排队抢夺监视器Owner权利的资格,其状态从WAITING变成BLOCKED。

    (4) EntryList中的线程抢夺到监视器的Owner权利之后,线程的状态从BLOCKED变成Runnable,具备重新执行的资格。

    (1) Owner 线程发现条件不满足,调用wait 方法,即可进入WaitSet,变为 WAITING 状态 ;

    (2) BLOCKED 和WAITING 的线程都处于阻塞状态,不占用CPU时间片 ;

    (3) BLOCKED:线程会在Owner 线程释放锁时唤醒 ;

    (4) WAITING :线程会在Owner 线程调用notify 或 notifyAll时唤醒,但唤醒后并不意味者立刻获得锁,仍需进入 EntryList 重新竞争;

    5. wait方法和notify方法示例

    1、进入Object监视器的线程才能调用wait()方法

    小南并不能直接进入WaitSet休息室,而是获取锁进入房间后才能进入休息室,没有锁的话小南没法进入房间,更没法进入休息室。他进入休息室后就会释放锁,让其他线程竞争锁进入房间。

    public class Main {    static final Object lock = new Object();    public static void main(String[] args) {        synchronized (lock){            try {                // 只有成为了monitor对象的owner,即获得了对象锁之后,才有资格进入waitset等待                lock.wait();            } catch (InterruptedException e) {                e.printStackTrace();            }        }    }}

    2、进入Object监视器的线程才能调用notify()方法

    小M此时获取到了锁,进入了房间,并唤醒了在休息室中等待的小王,小M如果获取到锁进行房间时没有办法唤醒在休息室等待的小王的,因为此时小M在门外,小王根本听不到。

    使用notify()唤醒等待区的一个线程:

    public class Main {    static final Object lock = new Object();    public static void main(String[] args) throws InterruptedException {        Thread t1 = new Thread(()->{            System.out.println("t1线程开始执行...");            synchronized (lock){                try {                    // 让t1线程在lock锁的waitset中等待                    lock.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }                // 被唤醒后,继续执行                System.out.println("线程t1被唤醒...");            }        },"t1");        t1.start();        Thread t2 = new Thread(()->{            System.out.println("t2线程开始执行...");            synchronized (lock){                try {                    // 让t2线程在lock锁的waitset中等待                    lock.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            // 被唤醒后,继续执行            System.out.println("线程t2被唤醒...");        },"t2");        t2.start();        Thread.sleep(2000);        System.out.println("唤醒lock锁上等待的线程...");        synchronized (lock){            // 主线程拿到锁后,唤醒正在休息室中等待的某一个线程            lock.notify();        }    }}

    执行结果:

    t1线程开始执行...
    t2线程开始执行...
    唤醒lock锁上等待的线程...
    线程t1被唤醒...

    使用notifyAll()唤醒等待区所有的线程:

    public class Main {    static final Object lock = new Object();    public static void main(String[] args) throws InterruptedException {        Thread t1 = new Thread(()->{            System.out.println("t1线程开始执行...");            synchronized (lock){                try {                    // 让t1线程在lock锁的waitset中等待                    lock.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }                // 被唤醒后,继续执行                System.out.println("线程t1被唤醒...");            }        },"t1");        t1.start();        Thread t2 = new Thread(()->{            System.out.println("t2线程开始执行...");            synchronized (lock){                try {                    // 让t2线程在lock锁的waitset中等待                    lock.wait();                } catch (InterruptedException e) {                    e.printStackTrace();                }            }            // 被唤醒后,继续执行            System.out.println("线程t2被唤醒...");        },"t2");        t2.start();        Thread.sleep(2000);        System.out.println("唤醒lock锁上等待的线程...");        synchronized (lock){            // 主线程拿到锁后,唤醒正在休息室中等待的所有线程            lock.notifyAll();        }    }}

    执行结果:

    t1线程开始执行...
    t2线程开始执行...
    唤醒lock锁上等待的线程...
    线程t2被唤醒...
    线程t1被唤醒...

    6. 为什么 wait 和 notify 方法要在同步块中调用?

    在调用同步对象的wait()和notify()系列方法时,"当前线程"必须拥有该对象的同步锁,也就是说,wait()和notify()系列方法需要在同步块中使用,否则JVM会抛出类似如下的异常:

    为什么wait和notify不在synchronized同步块的内部使用会抛出异常呢?这需要从wait()和notify()方法的原理说起。

    wait()方法的原理:

    首先,JVM会释放当前线程的对象锁监视器的Owner资格;其次,JVM会将当前线程移入监视器的WaitSet队列,而这些操作都和对象锁监视器是相关的。所以,wait()方法必须在synchronized同步块的内部调用。在当前线程执行wait()方法前,必须通过synchronized()方法成为对象锁的监视器的Owner。

    notify()方法的原理:

    JVM从对象锁的监视器的WaitSet队列移动一个线程到其EntryList队列,这些操作都与对象锁的监视器有关。所以,notify()方法也必须在synchronized同步块的内部调用。在执行notify()方法前,当前线程也必须通过synchronized()方法成为对象锁的监视器的Owner。

    关于"Java线程通信中wait-notify通信的方式是什么"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注行业资讯频道,小编每天都会为大家更新不同的知识点。

    线程 方法 对象 监视器 监视 同步 通信 休息室 房间 状态 方式 原理 时间 正在 资格 队列 作用 实例 权利 知识 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 云南咖啡 伟宏互联网科技 科技股互联网 麻工互联网科技 善班互联网科技 西安互联网科技公司 互联网金融科技委员会 卫星互联网科技股票 南山科技园互联网公司有哪些 知域互联网科技有限公司大骗子 成都互联网科技公司工作好不好 合肥互联网科技公司公章图片 美国互联网科技实力 科技互联网的核心竞争力 云南省自由互联网科技有限公司 同城互联网络科技 数字互联网科技有限公司 互联网企业和网络科技有限公司 高科技和互联网的现代国家 中国主要互联网科技公司 深圳市人人互联网科技有限公司 百单网互联网科技有限公司怎么样 深圳互联网科技公司 中国 科技 互联网 无锡溥仪互联网科技有限公司 互联网科技创新服务 深圳育鹏互联网科技有限公司招聘 明优互联网科技有限公司 烟台灵鸽互联网科技有限公司 广东工业互联网大赛深圳学泰科技
    0