wait() 、notify()、notifyAll() 的详细用法

文章目录

  • 💐wait() 讲解
  • 💐notify() 讲解
  • 💐notifyAll()
  • 💡wait() 和 sleep() 的区别

首先,我们知道,线程的执行顺序是随机的(操作系统随机调度的,抢占式执行),但是有时候,我们并不喜欢这种随机的执行,更喜欢的是它们能够顺序的执行,所以,Java就引入了一种机制,wait() 和 notify() ,它们的 作用就是保证线程执行的顺序;

而前面的文章中也讲过一个方法 join(),也是能影响线程的执行顺序,但是呢,这个join只能控制线程结束的顺序,而我们想要的是,线程不结束,也能按照我们自己规定的顺序去依次执行;

💐wait() 讲解

使用 wait() 时要注意一定要搭配 synchronized 使用,否则的话就会抛出异常

在这里插入图片描述

调用 wait() 时,wait() 会做三件事:

  • 使当前的线程处于阻塞等待
  • 释放当前线程获取到的锁
  • 在其他线程中使用锁对象调用notify时或者使用带参数的wait(带有时间参数,超过时间就会被唤醒)被唤醒后,会再重新尝试获取锁
public class Main {public static void main(String[] args) {Object locker = new Object();Thread thread1 = new Thread(() -> {synchronized (locker) {System.out.println("调用wait(), 阻塞等待");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread1被唤醒");}});thread1.start();}
}

在这里插入图片描述

可以看到,当执行到 wait() 这行代码时,就一直处于了阻塞等待,需要在其他线程中使用 notify() 来唤醒,而目前代码中没有其他线程,所以就一直等待;这里也需要解释一下,关于调用 wait() 方法,因为 wait() 方法是Object类中的方法,所以,所有的对象都可以调用;

💐notify() 讲解

notify 作用:唤 醒其它调用了 wait() 导致阻塞等待的线程;

1. wati() 和 notify() 都要在加锁的代码块中使用,并且由锁对象调用

2. 使用 notify() 唤醒某个线程时,只能唤醒和调用 notify() 是同一个锁的线程

比如:thread1 线程中使用 锁对象 locker1 调用了 wait() 方法阻塞等待,那么在其他线程中,也要使用 locker1 来调用 notify() 方法对 thread1 进行唤醒

 public static void main(String[] args) throws InterruptedException {Object locker = new Object();Thread thread1 = new Thread(() -> {synchronized (locker) {System.out.println("thread1执行,调用wait,进行阻塞,同时锁被释放");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread1执行完");}});Thread thread2 = new Thread(() -> {//先进行一个睡眠,可以明显的观察效果try {Thread.sleep(100);//让thread1先执行System.out.println("thread2执行,进入睡眠四秒");Thread.sleep(4000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker) {System.out.println("四秒后");System.out.println("进行唤醒");locker.notify();}});thread1.start();thread2.start();}

在这里插入图片描述

wait() 、notify() 也可以避免“线程饿死”

举个例子:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

💐notifyAll()

如果多个线程都调用 wait() 的话,多个线程都会进入阻塞,调用 notify() 的话,就只能唤醒一个,但是用 notifyAll() 的话,就可以一次性全部唤醒(唤醒的是等待 同一个锁的所有线程),这里要注意一点:多个线程被同时唤醒时,只有一个线程可以成功的获取到锁,其他的线程进行继续等待

场景:有四个线程,thread1,thread2,thread3 调用 wait() 进行阻塞等待,thread4 调用 notify(),最终,三个阻塞的线程只会有一个被唤醒,代码如下:

public class Main {public static void main(String[] args) {Object locker = new Object();Thread thread1 = new Thread(() -> {synchronized (locker) {System.out.println("thread1调用wait(), 阻塞等待");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread1被唤醒");}});Thread thread2 = new Thread(() -> {synchronized (locker) {System.out.println("thread2调用wait(), 阻塞等待");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread2被唤醒");}});Thread thread3 = new Thread(() -> {synchronized (locker) {System.out.println("thread3调用wait(), 阻塞等待");try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("thread3被唤醒");}});Thread thread4 = new Thread(() -> {try {Thread.sleep(5000);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker) {locker.notify();System.out.println("thread4调用notify()");}});thread1.start();thread2.start();thread3.start();thread4.start();}
}

执行结果:

在这里插入图片描述

如果将 notify() 换成 调用 notifyAll(),那么就会全部被唤醒,代码如下:

        Thread thread4 = new Thread(() -> {try {Thread.sleep(3);} catch (InterruptedException e) {throw new RuntimeException(e);}synchronized (locker) {locker.notifyAll();System.out.println("thread4调用notifyAll()");}}

通过结果可以看到,当 thread4 调用了 notifyAll() 后, thread3 先获取到了锁,然后释放锁后,thread2 又获取到了锁,最后是 thread1

在这里插入图片描述

💡wait() 和 sleep() 的区别

  • 类不同:sleep() 是Thread线程类的静态方法, wait() 是 Object类的方法
  • 调用后是否释放锁: sleep() 调用后不会有释放锁的操作; wait() 调用后会释放锁
  • 用途不同: wait() 通常用于线程间交互/通信, sleep() 通常用于暂停执行
  • 用法不同:wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用 notify() 方法, 或者 notifyAll() 方法 或者是使用wait(long timeout),指定一个阻塞时间,超时后线程自动苏醒。sleep() 方法执行完后,线程会自动苏醒。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/513051.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

详解C#之WinForm版利用RichTextBox 制作文本编辑器【附源码】

在Windows应用程序开发中,刚刚介绍了WPF版的利用RichTextBox实现文本编辑器,今天继续推出WinForm版的利用RichTextBox实现文本编辑器。本文利用一个简单的小例子,简述如何在WinForm开发中,利用RichTextBox开发文本编辑器&#xff…

day04-SpringBootWeb入门

一、SpringBootWeb快速入门 1 需求 需求:基于 SpringBoot 的方式开发一个 web 应用,浏览器发起请求 /hello后,给浏览器返回字符串“Hello World ~”。 2 开发步骤 第1步:创建 SpringBoot 工程项目 第2步:定义 HelloC…

Nodejs 第四十六章(redis持久化)

redis持久化 Redis提供两种持久化方式: RDB(Redis Database)持久化:RDB是一种快照的形式,它会将内存中的数据定期保存到磁盘上。可以通过配置Redis服务器,设置自动触发RDB快照的条件,比如在指…

低代码与数字经济:推动软件开发创新的新引擎

随着数字经济的飞速发展,传统的软件开发方式已经难以满足快速变化的市场需求。在这一背景下,低代码开发平台应运而生,以其高效、灵活的特性,成为推动数字经济创新发展的重要力量。本文将探讨低代码与数字经济的关系,分…

在 Flutter 中使用 flutter_gen 简化图像资产管理

你是否厌倦了在 Flutter 项目中手动管理图像资产的繁琐任务? 告别手工输入资源路径的痛苦,欢迎使用“Flutter Gen”高效资源管理的时代。在本文中,我将带您从手动处理图像资源的挫折到动态生成它们的便利。 选择1:痛苦手动添加–…

自动化测试环境搭建--Linux内网环境【实操经验】

环境信息 Python版本3.6.8 Jenkins版本2.346.3 Allure版本 2.13.2 环境准备 一、Python需要安装库 allure-pytest (2.13.2) pytest (7.0.1) jsonpath (0.82.2) pytest-html (3.2.0) pytest-ordering (0.6) …

Pytorch学习 day03(Tensorboard、Transforms)

Tensorboard Tensorboard能够可视化loss的变化过程,便于我们查看模型的训练状态,也能查看模型当前的输入和输出结果 在Pycharm中,可以通过按住ctrl,并左键点击某个库来进入源文件查看该库的使用方法SummaryWriter是用来向log_dir…

USB - Linux Kernel Menuconfig

Linux kernel,make menuconfig,和USB相关的,在主菜单选择Device Drivers。 Device Drivers下面,找到USB support。 在USB support下面,就可以对USB相关的item进行设置。 按照从上到下的顺序,打开的设置依次…

数据结构学习(四)高级数据结构

高级数据结构 1. 概念 之所以称它们为高级的数据结构,是因为它们的实现要比那些常用的数据结构要复杂很多,能够让我们在处理复杂问题的过程中, 多拥有一把利器,同时掌握好它们的性质,以及所适应的场合,在…

Linux常用命令(超详细)

一、基本命令 1.1 关机和重启 关机 shutdown -h now 立刻关机 shutdown -h 5 5分钟后关机 poweroff 立刻关机 重启 shutdown -r now 立刻重启 shutdown -r 5 5分钟后重启 reboot 立刻重启 1.2 帮助命令 –help命令 shutdown --help: ifconfig --help:查看…

大模型总结

抛开大模型基座训练,我们还可以关注什么? - 知乎 大模型LLM领域,有哪些可以作为学术研究方向? 方向一:大模型的基础理论问题 大力出奇迹,涌现,目前还需要科学家继续研究理论基础 也就是先有…

Spring中@import注解终极揭秘!

技术概念 它能干啥 Import注解在Spring框架中主要用于解决模块化和配置管理方面的技术问题,它可以帮助开发者实现以下几个目标: 模块化配置:在大型项目中,通常需要将配置信息分散到多个配置类中,以便更好地组织和管…