【Java|多线程与高并发】wait和notify方法详解

简介: 在Java多线程环境中,线程之间是抢占式执行的,线程的调度是随机的.这就很难受了. 在很多情况下我们希望线程以我们想要的顺序来执行. 这就需要wait和notify这两个方法

1.前言

在Java多线程环境中,线程之间是抢占式执行的,线程的调度是随机的.这就很难受了. 在很多情况下我们希望线程以我们想要的顺序来执行. 这就需要wait和notify这两个方法


59e74b222c014ea48c8781bd1e93dea1.gif


2.wait和notify的基本使用

首先是wait方法



736769a8005645fb985f554ba91dc32b.png

wait是Object类的方法,而Java中的类都是间接或直接继承于Object类. 因此只要是类的实例都可以调用wait方法


运行上述代码:

12a82e2f34594edfb0f5cef7439fed4a.png



可以看到这里抛出了一个 非法的锁状态异常


其实wait方法的执行分为三步:


1.释放当前锁

2.进行等待通知

3.满足一定的条件(其它线程调用notify),被唤醒,然后重新获取锁

什么要先释放锁,再进行等待通知呢?


因为wait如果不释放锁,可能会影响到其它线程的执行,就是为了保证不影响其它线程的执行


因此wait与锁密不可分

09135b0ed76f4582a16289021bd61180.png



对上述代码进行加锁,就不会抛出异常了.


但上述代码的执行并没有结束,因为线程在调用wait方法之后,会一直处于阻塞状态,直到有其它线程调用notify方法为止.

而调用notify方法的线程要和调用wait方法的线程是针对同一个对象进行加锁的才行.


示例:

    public static void main(String[] args) {
        Object object = new Object();
        Thread t1 = new Thread(() ->{
            while(true){
                synchronized (object){
                    System.out.println("wait 之前");
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    System.out.println("wait 之后");
                }
            }
        });
        t1.start();
        Thread t2 = new Thread(() ->{
            while(true){
                synchronized (object){
                    System.out.println("notify 之前");
                    object.notify();
                    System.out.println("notify 之后");
                }
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t2.start();
    }

运行截图:

736966ed0cbe4c5da5711b3ad34b8838.png



上述标注的是一组 wait和notify方法的执行流程.


注: 如果先调用的是notify方法,代码并不会有什么影响.


3. notifyAll方法

Java中除了使用notify方法唤醒线程,还有一个notifyAll方法.

当一个线程调用了某个对象的notifyAll方法后,该对象上的所有等待线程将被唤醒,即使唤醒了所有在等待的线程,这些线程之间也要进行锁竞争(串行执行)。


4. wait和sleep方法的对比

wait方法和sleep方法的对比也是面试中经常会问到的问题


对于这个问题,可以从相同点和不同点进行回答:


相同点: 都是让线程进入阻塞等待状态

不同点:sleep方法是通过时间来通知唤醒,而wait方法则需要使用notify或notifyAll进行唤醒


5. 总结

wait和notify方法用于实现线程间的协作和通信.

wait方法使线程进入等待状态,notify方法唤醒一个等待线程,notifyAll方法唤醒所有等待线程。

通过上述三个方法,可以使线程按照特定的顺序执行或者等待某个条件满足后再执行。

9a81996bb66b41ceb0fa838d5b6aafcc.gif


相关文章
|
5天前
|
存储 Oracle Java
|
6天前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
|
1月前
|
算法 Java 开发者
Java 项目实战数字华容道与石头迷阵游戏开发详解及实战方法
本文介绍了使用Java实现数字华容道和石头迷阵游戏的技术方案与应用实例,涵盖GUI界面设计、二维数组操作、游戏逻辑控制及自动解法算法(如A*),适合Java开发者学习游戏开发技巧。
185 46
|
2月前
|
Java
创建线程的方法
Java中实现多线程有四种方式:1. 继承Thread类,简单但占用继承机会,耦合度高;2. 实现Runnable接口,推荐方式,任务与线程解耦,支持Lambda;3. 实现Callable接口配合FutureTask,可获取返回值和异常;4. 使用线程池(ExecutorService),企业推荐,管理线程生命周期,提升性能,支持多种线程池类型。
61 1
|
2月前
|
安全 Java API
Java 集合高级应用与实战技巧之高效运用方法及实战案例解析
本课程深入讲解Java集合的高级应用与实战技巧,涵盖Stream API、并行处理、Optional类、现代化Map操作、不可变集合、异步处理及高级排序等核心内容,结合丰富示例,助你掌握Java集合的高效运用,提升代码质量与开发效率。
193 0
|
2月前
|
算法 搜索推荐 Java
Java中的Collections.shuffle()方法及示例
`Collections.shuffle()` 是 Java 中用于随机打乱列表顺序的方法,基于 Fisher-Yates 算法实现,支持原地修改。可选传入自定义 `Random` 对象以实现结果可重复,适用于抽奖、游戏、随机抽样等场景。
106 0
|
2月前
|
安全 Java
JAVA:Collections类的shuffle()方法
`Collections.shuffle()` 是 Java 中用于随机打乱列表顺序的工具方法,适用于洗牌、抽奖等场景。该方法直接修改原列表,支持自定义随机数生成器以实现可重现的打乱顺序。使用时需注意其原地修改特性及非线程安全性。
112 0
|
2月前
|
算法 安全 Java
java中Collections.shuffle方法的功能说明
`Collections.shuffle()` 是 Java 中用于随机打乱列表顺序的方法,基于 Fisher-Yates 算法实现,常用于洗牌、抽奖等场景。可选 `Random` 参数支持固定种子以实现可重复的随机顺序。方法直接修改原列表,无返回值。
104 0
|
2月前
|
Java 程序员 项目管理
Java 程序员不容错过的 Git Flow 全套学习资料及应用方法详解 Git Flow
本文详细介绍了Git Flow技术方案及其在Java项目中的应用实例,涵盖分支管理、版本发布与紧急修复流程,帮助开发者掌握高效的代码管理方法,提升团队协作效率。附示例操作及代码下载链接。
76 0
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
423 1