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() 方法执行完后,线程会自动苏醒。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件测试面试题(全)

【软件测试面试突击班】2024吃透软件测试面试最全八股文攻略教程,一周学完让你面试通过率提高90%!(自动化测试) 1.B/S架构和C/S架构区别 B/S 只需要有操作系统和浏览器就行,可以实现跨平台,客户端零维护&a…

CSS全局样式的设置,web开发交流

面试题 HTML 1,html5有哪些新特性? 2,html5移除了那些元素? 3,如何处理HTML5新标签的浏览器兼容问题 戳这里领取完整开源项目:【一线大厂前端面试题解析核心总结学习笔记Web真实项目实战最新讲解视频】…

智能硬件 | AI PC新市场,英特尔、高通、AMD、苹果谁能拔得头筹?

我们普通人和大模型的距离有多远?AI发展到2024年,已经附着在各种智能硬件上了,什么AI PC,AI手机,AI蓝牙音箱,AI学习机,AI鼠标等等,但其实虽然很多产品加上了个AI的名头,但…

xss.haozi:0x00

0x00没有什么过滤所以怎么写都没有关系有很多解 <script>alert(1)</script>

实现session共享的方法总结完整版

文章目录 实现session共享的方法总结完整版1、使用共享数据库&#xff1a;2、使用粘性会话&#xff08;Sticky Session&#xff09;&#xff1a;3、使用缓存系统&#xff1a;4、使用分布式文件系统&#xff1a;5、使用中央认证服务&#xff1a;6、使用会话复制&#xff1a;7、使…

LLM 模型量化推理速度评测

最近了解了下些常见的推理和加速方案&#xff1a; 1、量化方案&#xff1a; gptq、quantization、int8、int4、AWQ、Speculative Decoding、GGUF 2、Attention加速方案&#xff1a; atten的不同种类fused attention 3、内存层面&#xff1a; kv_cache策略、page_attention…

3.4作业

课上代码复习&#xff1a; 广播接收端代码: #include<myhead.h> int main(int argc, const char *argv[]) {//创建套接字int rfd socket(AF_INET,SOCK_DGRAM,0);if(rfd -1){perror("socket error");return -1;}printf("rfd %d\n",rfd);//填充地…

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

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

达梦数据库基础操作(五): 索引操作

达梦数据库基础操作(五)&#xff1a; 索引操作 1. 索引操作 1.1 创建索引 # 使用 CREATE INDEX 语句创建普通索引。 CREATE INDEX ind_emp_salary ON employee(salary);1.2 查看创建的索引 # 通过字典表 user_indexes 查看已创建索引的名称、类型。SELECT table_name, index…

C语言经典算法-1

C语言经典算法讲解练习 文章目录 C语言经典算法讲解练习1.汉若塔2.费式数列3. 巴斯卡三角形4.三色棋5.老鼠走迷官&#xff08;一&#xff09;6.老鼠走迷官&#xff08;二&#xff09;7.骑士走棋盘8.八皇后9.八枚银币10.生命游戏 1.汉若塔 说明&#xff1a;河内之塔(Towers of …

ArmSoM Rockchip系列产品 通用教程 之 UART 使用

1. UART 简介​ Rockchip UART (Universal Asynchronous Receiver/Transmitter) 基于16550A串口标准&#xff0c;完整模块支持以下功能&#xff1a; 支持5、6、7、8 bits数据位。支持1、1.5、2 bits停止位。支持奇校验和偶校验&#xff0c;不支持mark校验和space校验。支持接…

文物保护平台数据统计分析及预警-子系统专题分析

文物预防性监测与调控系统的监测统计分析子系统提供全面的文物状态及环境数据分析,为博物馆工作人员进行基于文物材质特性的专项保护提供相关科研辅助。主要的监测分析,包括各展厅文物统计分析、不同环境因素报表统计、以及监测调控设备统计分析等。 系统用户和文物管理人员可以…

onnx runtime文档学习2-torch TF简单示例

网上充斥着ONNX Runtime的简单科普&#xff0c;却没有一个系统介绍ONNX Runtime的博客&#xff0c;因此本博客旨在基于官方文档进行翻译与进一步的解释。ONNX runtime的官方文档&#xff1a;https://onnxruntime.ai/docs/ 如果尚不熟悉ONNX格式&#xff0c;可以参照该博客专栏…

Nodejs 第四十七章(redis主从复制)

Redis主从复制是一种数据复制和同步机制&#xff0c;其中一个Redis服务器&#xff08;称为主服务器&#xff09;将其数据复制到一个或多个其他Redis服务器&#xff08;称为从服务器&#xff09;。主从复制提供了数据冗余备份、读写分离和故障恢复等功能。 以下是Redis主从复制的…

redis06 redis事务

思维草图 redis事务认识 redis事务是一个单独的隔离操作&#xff0c;事务中的所有命令都会序列化、按顺序地执行&#xff0c;事务在执行的过程中&#xff0c;不会被其他客户端发送来的命令请求所打断。 redis事务的主要作用就是串联多个命令防止别的命令插队。 Multi、Exec、…

稀碎从零算法笔记Day9-LeetCode:最长公共前缀

题型&#xff1a;字符串 链接&#xff1a;14. 最长公共前缀 - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述&#xff08;红字为笔者添加&#xff09; 编写一个函数来查找字符串数组中的最长公共前缀(前X个字母相同)。 如果不存在公共前缀&…

【数据结构和算法初阶(C语言)】复杂链表(随机指针,随机链表的复制)题目详解+链表顺序表结尾

目录 1.随机链表的复制 1.2题目描述 1.3题目分析 1.4解题&#xff1a; 2.顺序表和链表对比 2.1cpu高速缓存利用率 3.结语 1.随机链表的复制 一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random 该指针可以指向链表中的任何节点或空节点。 1.2题目描…

Discuz IIS上传附件大于28M失败报错Upload Failed.修改maxAllowedContentLength(图文教程)

下图&#xff1a;Discuz X3.5的系统信息&#xff0c;上传许可为1024MB(1GB) 论坛为局域网论坛&#xff0c;仅供内部同事交流使用&#xff01; 使用官方最新的Discuz! X3.5 Release 20231221 UTF-8 下图&#xff1a;选择上传附件&#xff08;提示可以最大上传100M&#xff09;…

【Unity】使用ScriptableObject存储数据

1.为什么要用ScriptableObject&#xff1f; 在游戏开发中&#xff0c;有大量的配置数据需要存储&#xff0c;这个时候就需要ScriptableObject来存储数据了。 很多人会说我可以用json、xml、txt&#xff0c;excel等等 但是你们有没有想过&#xff0c;假设你使用的是json&#x…

Python 面向对象编程——类的使用

一、学习目标 1&#xff0e;掌握类的定义和实例化对象。 2&#xff0e;熟练掌握类的构造函数__init__使用。 3&#xff0e;掌握类的继承机制和使用。 二、相关练习 1、定义一个玩具类Toy()&#xff0c;创建名字为“小汽车”、“手枪”和“积木”的玩具实例&#xff0c;计…