【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


相关文章
|
3天前
|
Java
Java的方法详解
Java的方法是类中的重要组成部分,用于定义类的行为。方法可以接收参数、执行操作并返回结果。其基本语法包括返回类型、方法名、参数列表和方法体。方法支持重载,即同名但参数不同的多个方法;静态方法则直接通过类名调用,无需实例化。此外,Java还支持可变参数,允许方法接收不定数量的参数。通过访问修饰符如`public`、`protected`、`private`,可以控制方法的可见性。方法是实现类功能的基本单元,增强了程序的灵活性和复用性。
|
10天前
|
存储 Java 程序员
优化Java多线程应用:是创建Thread对象直接调用start()方法?还是用个变量调用?
这篇文章探讨了Java中两种创建和启动线程的方法,并分析了它们的区别。作者建议直接调用 `Thread` 对象的 `start()` 方法,而非保持强引用,以避免内存泄漏、简化线程生命周期管理,并减少不必要的线程控制。文章详细解释了这种方法在使用 `ThreadLocal` 时的优势,并提供了代码示例。作者洛小豆,文章来源于稀土掘金。
|
12天前
|
Java Spring
运行@Async注解的方法的线程池
自定义@Async注解线程池
39 3
|
1天前
|
JavaScript 前端开发 Java
通过JUnit5访问Java静态、私有、保护变量和方法
在《通过Gtest访问C++静态、私有、保护变量和方法》一文中介绍了如何通过Gtest访问C++静态、私有、保护变量和方法,本文介绍如何通过Junit5访问Java静态、私有、保护变量和方法。
6 0
|
算法 Java Linux
每日一面 - java里的wait()和sleep()的区别有哪些?
每日一面 - java里的wait()和sleep()的区别有哪些?
每日一面 - java里的wait()和sleep()的区别有哪些?
|
Java 调度
java中wait和sleep的区别
java中wait和sleep的区别
|
11天前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
73 6
【Java学习】多线程&JUC万字超详解
|
4天前
|
Java 调度 开发者
Java并发编程:深入理解线程池
在Java的世界中,线程池是提升应用性能、实现高效并发处理的关键工具。本文将深入浅出地介绍线程池的核心概念、工作原理以及如何在实际应用中有效利用线程池来优化资源管理和任务调度。通过本文的学习,读者能够掌握线程池的基本使用技巧,并理解其背后的设计哲学。
|
4天前
|
缓存 监控 Java
Java中的并发编程:理解并应用线程池
在Java的并发编程中,线程池是提高应用程序性能的关键工具。本文将深入探讨如何有效利用线程池来管理资源、提升效率和简化代码结构。我们将从基础概念出发,逐步介绍线程池的配置、使用场景以及最佳实践,帮助开发者更好地掌握并发编程的核心技巧。
|
6天前
|
缓存 监控 Java
java中线程池的使用
java中线程池的使用