JAVA中wait()和notify()如何使用详解

 更新时间:2025年05月08日 10:28:41   作者:Lizhihao_  
这篇文章主要介绍了JAVA中wait()和notify()如何使用的相关资料,wait和notify必须在同步方法或同步块中使用,并且调用对象必须一致,wait和sleep都可被interrupt唤醒,但wait会释放锁,而sleep不会,需要的朋友可以参考下

前言

大家应该都知道,线程之间是抢占式随机执行的,但是我们并不希望这样。因为这样非常混乱,并不好去预估程序的执行结果。我们甚至希望,线程之间能够配合执行,那么我们就可以使用wait()和notify()来做到。

一、wait()方法

wait有两个方法:

wait():让当前线程进入等待(阻塞)状态。死等,没有唤醒就会一直阻塞

wait(long timeout) :指定时间内,让线程进入等待(阻塞)状态。

1.wait()主要做的事

  • 使当前执行代码的线程进行等待。(把线程放到等待队列中)
  • 释放当前的锁
  • 满足条件被notify()唤醒,重新尝试获取这个锁
    public static void main(String[] args) throws InterruptedException {
        Object object = new Object();

        System.out.println("wait 之前");

        /**
         * Object中有wait方法,通过Object对象调用wait方法
         * wait没有参数的版本是默认死等
         * wait带参数的版本是指定超时时间,如果超时了还没有notify就继续执行
         *
         * 使用wait有三个操作:
         * (1)释放锁
         * (2)进入阻塞等待,准备接受通知
         * (3)收到通知之后唤醒,并且重新获取锁
         */

        /**
         * 因为wait会解锁,所以wait必须在synchronized内部
         * 而且synchronized括号内的Object对象,必须和调用wait的是同一对象
         */
        synchronized(object) {
            object.wait();
        }
        System.out.println("wait 之后");

    }

2.wait()的结束条件

其他线程调⽤该对象的 notify ⽅法.

wait 等待时间超时 (wait ⽅法提供⼀个带有 timeout 参数的版本, 来指定等待时间).

其他线程调⽤该等待线程的 interrupt ⽅法, 导致 wait 抛出 InterruptedException 异常,并清除中断标志位(给程序员自由发挥的空间),并重新尝试获取锁。
    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();

        Thread t1 = new Thread(() -> {
            //获取当前线程
            Thread current = Thread.currentThread();
            int count = 0;
            //标志位默认为false,为true就是被中断了
            while (!current.isInterrupted()) {
                //如果中断位被清空了,不会执行第二次
                System.out.println("t1线程第一次执行" + count++);
                synchronized (locker) {
                    //因为Thread并没有处理这个异常,所以必须在这里使用try-catch处理一下
                    try {
                        //开始等待
                        //如果被interrupt中断会抛出异常,并清除中断位
                        //并重新尝试获取锁
                        locker.wait();
                    } catch (InterruptedException e) {
                        System.err.println("被interrupt唤醒");
                    }
                }
            }
        });

        t1.start();
        while (true) {
            Thread.sleep(1000);
            t1.interrupt();
        }
    }

3.有参数的wait()

    public static void main(String[] args) {
        Object locker = new Object();

        Thread t1 = new Thread(() -> {
            long start = System.currentTimeMillis();
            System.out.println("wait之前");
            synchronized (locker) {
                try {
                    locker.wait(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            long end = System.currentTimeMillis();
            System.out.println("wait之后 耗时:" + (end - start));
        });

        t1.start();

    }

结果:

二、notify()

notify⽅法是唤醒等待的线程,主要配合wait():

notify():唤醒同一对象调用正在wait()的线程,如果有多个线程正在wait()就会随机唤醒一个线程

notifyAll():唤醒所有正在wait()的线程

1.notify()主要做的事

⽅法notify()也要在同步⽅法或同步块中调⽤,该⽅法是⽤来通知那些可能等待该对象的对象锁的其它线程,对其发出通知notify,并使它们重新获取该对象的对象锁。

如果有多个线程等待,则有线程调度器随机挑选出⼀个呈 wait 状态的线程。(并没有 "先来后到")

在notify()⽅法后,当前线程不会⻢上释放该对象锁,要等到执⾏notify()⽅法的线程将程序执⾏完,也就是退出同步代码块之后才会释放对象锁。

    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();


        Thread t1 = new Thread(() -> {
            synchronized (locker) {

                System.out.println("wait之前~");

                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("wait之后!");
            }
        });


        Thread t2 = new Thread(() -> {
            synchronized (locker) {

                System.out.println("notify之前");

                locker.notify();

                //notify之后,并不会马上释放锁结束,至少会
                //把synchronized中的语句执行完
                System.out.println("未解锁之后的notify");
            }
            //看是否会执行到这一条语句
            System.err.println("解锁之后的notify");
        });

        t1.start();
        //防止执行到notify了,t2还没阻塞(wait)
        Thread.sleep(500);
        t2.start();

    }

结果分析:

注意事项:

2.notify() 会唤醒sleep()吗?

    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();

        Thread t1 = new Thread(() -> {
            System.out.println("sleep睡眠");
            try {
                //sleep不需要在synchronized里
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("sleep睡醒了");
        });

        Thread t2 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("notify之前");
                locker.notify();
                System.out.println("notify之后");
            }
        });

        t1.start();
        Thread.sleep(1000);
        t2.start();
    }

结果:

3.notifyAll()

 唤醒所有正在等待的线程

    public static void main(String[] args) throws InterruptedException {
        Object locker = new Object();
        Thread t1 = new Thread(() -> {
            System.out.println("t1线程开始");
            synchronized (locker) {
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.err.println("t1线程结束");
        });
        Thread t2 = new Thread(() -> {
            System.out.println("t2线程开始");
            synchronized (locker) {
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.err.println("t2线程结束");
        });
        Thread t3 = new Thread(() -> {
            System.out.println("t3线程开始");
            synchronized (locker) {
                try {
                    locker.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.err.println("t3线程结束");
        });
        Thread t4 = new Thread(() -> {
            synchronized (locker) {
                System.out.println("notify之前");
                locker.notifyAll();
                System.out.println("notify之后");
            }
        });

        t1.start();
        t2.start();
        t3.start();
        //保证上面的线程都已经执行了wait
        Thread.sleep(1000);
        t4.start();
    }

三、调用 wait\notify\synchronized 使用的对象

 注意:wait和notify都必须放在synchronized中,不然会抛出异常:IllegalMonitorStateException

    public static void main(String[] args) {
        Object locker = new Object();

        Thread t1 = new Thread(() -> {
            System.out.println("t1线程开始");
            try {
                locker.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.err.println("t1线程结束");
        });

        Thread t4 = new Thread(() -> {
            try {
                Thread.sleep(1000);
                locker.notify();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        t1.start();
        t4.start();
    }

并且synchronized括号内部,必须和调用的是同一对象,不然依然会抛异常:

    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();

        Thread t1 = new Thread(() -> {
            synchronized (locker1) {
                System.out.println("t1线程开始");
                try {
                    locker2.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.err.println("t1线程结束");
            }
        });

        Thread t4 = new Thread(() -> {
            synchronized (locker1) {
                try {
                    Thread.sleep(1000);
                    locker2.notify();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });

        t1.start();
        t4.start();
    }

四、wait和sleep的比较/区别(面试题)

其实理论上 wait 和 sleep 完全是没有可⽐性的,因为⼀个是⽤于线程之间的通信的,⼀个是让线程阻塞⼀段时间。

相同点:

都会让线程放弃执行一段时间

都可以被interrupt唤醒,并且都会抛出 InterruptedException 异常,并且清空标志位

不同点:

wait是Object的方法,sleep是Thread的静态方法

wait必须在synchronized中,sleep不需要

wait阻塞的时候释放锁,sleep并不会,sleep会抱着锁一起阻塞

wait用于线程间通信(如生产者-消费者模型),sleep用于阻塞线程

总结

到此这篇关于JAVA中wait()和notify()如何使用的文章就介绍到这了,更多相关Java wait()和notify()内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java NIO实现聊天功能

    Java NIO实现聊天功能

    这篇文章主要为大家详细介绍了Java NIO实现聊天功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • Java虚拟机运行时栈的栈帧

    Java虚拟机运行时栈的栈帧

    本节将会介绍一下Java虚拟机栈中的栈帧,会对栈帧的组成部分(局部变量表、操作数栈、动态链接、方法出口)分别进行介绍,最后还会通过javap命令反解析编译后的.class文件,进行分析方法执行时的局部变量表、操作数栈等
    2021-09-09
  • springboot多模块多环境配置文件问题(动态配置生产和开发环境)

    springboot多模块多环境配置文件问题(动态配置生产和开发环境)

    这篇文章主要介绍了springboot多模块多环境配置文件问题(动态配置生产和开发环境),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-04-04
  • 从零开始Mybatis连接数据库的方法

    从零开始Mybatis连接数据库的方法

    这篇文章主要介绍了Mybatis连接数据库的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-02-02
  • Java一行代码搞定耗时性能追踪

    Java一行代码搞定耗时性能追踪

    在开发过程中,性能监控和调试是我们经常面对的问题,虽然市面上有许多成熟的性能监控工具,但有时我们需要一个轻量级、灵活且优雅的解决方案,下面我们就来看看Java如何搞定耗时性能追踪吧
    2024-11-11
  • Spring boot通过HttpSessionListener监听器统计在线人数的实现代码

    Spring boot通过HttpSessionListener监听器统计在线人数的实现代码

    这篇文章主要介绍了Spring boot通过HttpSessionListener监听器统计在线人数的实现代码,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2018-02-02
  • Java的JSTL标签库详解

    Java的JSTL标签库详解

    JSTL包含用于编写和开发JSP页面的一组标准标签,它可以为用户提供一个无脚本环境。在此环境中,用户可以使用标签编写代码,而无须使用Java脚本
    2023-05-05
  • Spring Security自定义登录原理及实现详解

    Spring Security自定义登录原理及实现详解

    这篇文章主要介绍了Spring Security自定义登录原理及实现详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-09-09
  • 基于Javamail实现发送邮件(QQ/网易邮件服务器)

    基于Javamail实现发送邮件(QQ/网易邮件服务器)

    这篇文章主要介绍了基于Javamail实现发送邮件,分别使用QQ邮箱作为smtp邮件服务器发送邮件,使用网易邮箱作为smtp邮件服务器发送邮件,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-08-08
  • Java重载构造原理与用法详解

    Java重载构造原理与用法详解

    这篇文章主要介绍了Java重载构造原理与用法,结合实例形式分析了java可变参数、方法重载、构造器等相关概念、原理及操作注意事项,需要的朋友可以参考下
    2020-02-02

最新评论