wait和notify : 避免线程饿死(以及votile内存可见性和指令重排序问题)

各位看官,大家早安午安晚安呀~~~

如果您觉得这篇文章对您有帮助的话

欢迎您一键三连,小编尽全力做到更好
欢迎您分享给更多人哦

今天我们来学习:wait和notify : 避免线程饿死(以及votile内存可见性和指令重排序问题)

目录

4.volatile

4.1.保证内存可见性

4.2.:指令重排序问题(我们放到下一篇博客来讲解)

5.wait,notify

5.1.wait方法的使用

5.2.notify方法的使用

6.wait和notify可以避免线程饿死


4.volatile

volatile的两个作用(其实都是不让编译器做出多余的优化,对我们的程序产生bug)

1.保证内存可见性

2.解决指令重排序问题

4.1.保证内存可见性

序言:

计算机运行的程序往往要访问数据,这些依赖的数据一般都被读取到了内存里面(譬如我们定义的一个变量就是在内存里面)

就譬如count++;cpu先把count读到cpu寄存器里面再进行++;

但是!  cpu其他的操作都很快,但是读内存就比较慢(读硬盘更慢,快慢都是相对的)

因此,为了提高效率编译器就会做出优化,把一些要读内存的操作优化成读寄存器。减少读内存的次数,就提高了整体程序的效率~~

public class Demo1 {private static int isQuit = 0;public static void main(String[] args) {Thread t1 = new Thread(() ->{while(isQuit == 0){// 一直循环啥也不不干}System.out.println("t1 退出");});Thread t2 = new Thread(()->{System.out.println(" 请输入isQuit的值");Scanner scanner = new Scanner(System.in);isQuit = scanner.nextInt(); // 输入不为0的值,使t1线程结束});t1.start();t2.start();}
}

我们发现我们明明输入非0值但是t1线程就是没结束为什么呢?

所以说volatile就是一个优化方法,我们给isQuit前面用volatile修饰就可以避免这样的问题了

       在多线程环境下,编译器对于是否要进行这样的优化,判定不一定准.这个时候就需要程序猿通过 volatile 关键字,告诉编译器,这个情况不用优化!!!优化,是算的快了,但是算的不准了)

 总之:编译器,也不是万能的.也会有一些自己短板的地方.此时就需要程序猿进行补充了.
只需要给 isQuit加上 volatile 关键字修饰,此时编译器自然就会禁止上述优化过程。

还有另一种方式,也可以避免这样的问题(再加上一个时间更长的操作,让读内存不那么慢了(相对来说)哈哈)

public class Demo1 {private static int isQuit = 0;public static void main(String[] args) {Thread t1 = new Thread(() ->{while(isQuit == 0){// 一直循环啥也不不干try {   //就多加了一个sleepThread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t1 退出");});Thread t2 = new Thread(()->{System.out.println(" 请输入isQuit的值");Scanner scanner = new Scanner(System.in);isQuit = scanner.nextInt(); // 输入不为0的值,使t1线程结束});t1.start();t2.start();}
}

此时没加 volatile, 但是给循环里加了个 slee

线程是可以退出了
加了 sleep 之后, while 循环执行速度就慢了.由于次数少了,load (读内存)操作的开销,就不大了.因此,优化也就没必要进行了.
没有触发 load的优化,也就没有触发内存可见性问题了.
到底啥时候代码有优化,啥时候没有?也说不清楚,但是使用 volatile 还是更靠谱的选择!!

4.2.:指令重排序问题(我们放到下一篇博客来讲解)

5.wait,notify

每个线程都是独立的执行流

想法:

由于线程之间是抢占式执行的, 因此线程之间执行的先后顺序难以预知.(join是决定了线程结束的先后顺序)
但是实际开发中有时候我们希望合理的协调多个线程之间的执行先后顺序.

完成这个协调工作, 主要涉及到三个方法

wait() / wait(long timeout): 让当前线程进入等待状态.

notify() / notifyAll(): 唤醒在当前对象上等待的线程.

注意: wait, notify, notifyAll 都是 Object 类的方法.

5.1.wait方法的使用

wait()方法要做的事情:

  1. 释放当前的锁(前提是你得有锁,不然怎么释放)
  2. 线程进入阻塞
  3. 线程被唤醒, 重新尝试获取这个锁.

wait 要搭配 synchronized 来使用. 脱离 synchronized 使用 wait 会直接抛出异常.

  public static void main(String[] args) throws InterruptedException {Object locker = new Object();System.out.println("wait 之前");locker.wait();  //没加锁,会抛出异常System.out.println("wait 之后");}

wait 结束等待的条件:

1.其他线程调用该对象的 notify 方法.

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

3.其他线程调用该等待线程的 interrupted 方法, 导致 wait 抛出 InterruptedException 异常.

接下来我们看一个代码

  public static void main(String[] args) throws InterruptedException {Object locker = new Object();System.out.println("wait 之前");locker.wait();  // 只要一般涉及阻塞的方法都会抛出异常//locker这个对象只要不notify,这个线程就一直处于waiting状态(加了等待时间就不会了)System.out.println("wait 之后");}

运行结果以及jconsole观察到的结果(这个程序就一直等待,死等(除非有等待时间))

5.2.notify方法的使用

我们以代码举例:创建三个线程都去wait,主线程notify唤醒(随机的)

  public static void main(String[] args) throws InterruptedException {Object locker = new Object(); // 公用一个锁对象Thread t1 = new Thread(() ->{synchronized (locker){try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t1 醒了");});Thread t2 = new Thread(()-> {synchronized (locker){try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t2 醒了");});Thread t3 = new Thread(()->{synchronized(locker){try {locker.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t2 醒了");});t1.start();t2.start();t3.start();System.out.println("唤醒其中一个线程");Thread.sleep(1000);  // 确保其他线程已经处于waiting状态synchronized(locker){locker.notify();// 也要记得加锁呀,不然咋释放锁}}

结果:可以看到随机一个线程被唤醒,其他线程在waiting状态

如果是notifyAll方法呢?(三个线程都被唤醒了,但是第一个拿到锁的线程如果执行的任务比较多,另外两个线程会处于BLOCKED状态(由于锁竞争导致的阻塞))

6.wait和notify可以避免线程饿死

先介绍synchronized是可重入锁,什么是可重入锁呢?一个线程对一个对象连续加锁两次不会出现死锁。就譬如这个按理说会出现死锁但是我是可重入的所以不会形成死锁

说到死锁:举一个例子()(这样一个线程想要多把锁,进而形成环,直接卡死就会导致死锁)

说到环就想到哲学家问题

总结:四个成因(破坏一个就解除)

 class Singleton {private static Singleton instance = new Singleton();public static Singleton getInstance(){  // 一定要是静态方法,不然一开始别人都拿不到这个对象,又没办法调用这个方法,岂不是贻笑大方return instance;}private Singleton(){} // 啥也不用干,也干了呀,把构造方法给藏起来了
}public class SingletonDemo{public static void main(String[] args) {Singleton singleton = new Singleton();}
}

上述就是wait和notify : 避免线程饿死(以及votile内存可见性和指令重排序问题)的全部内容啦

能看到这里相信您一定对小编的文章有了一定的认可。

有什么问题欢迎各位大佬指出
欢迎各位大佬评论区留言修正~~

您的支持就是我最大的动力​​​!!!

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

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

相关文章

HarmonyOS 介绍

HarmonyOS简介 随着万物互联时代的开启,应用的设备底座将从几十亿手机扩展到数百亿IoT设备。全新的全场景设备体验,正深入改变消费者的使用习惯。 同时应用开发者也面临设备底座从手机单设备到全场景多设备的转变,全场景多设备的全新底座&am…

【视觉提示学习】3.28阅读随想

2109.01134 CoOp通过可学习的向量来建模提示的上下文词汇,这些向量可以用随机值或预训练的词嵌入进行初始化(见图2)。我们提供了两种实现方式,以处理不同性质的任务:一种是基于统一上下文(unified context…

计算机求职面试中高频出现的经典题目分类整理

以下为计算机求职面试中高频出现的经典题目分类整理,涵盖技术核心与深度考察方向,答案要点已附解析思路: 一、数据结构与算法 链表操作 题目:反转链表(迭代/递归实现)考察点:指针操作、递归思维…

uniapp选择文件使用formData格式提交数据

1. Vue实现 在vue项目中,我们有个文件,和一些其他字段数据需要提交的时候,我们都是使用axios 设置请求头中的Content-Type: multipart/form-data,然后new FormData的方式来进行提交。方式如下: const sendRequest = () => {const formData = new FormData()formData…

BeanDefinition和Beanfactory实现一个简单的bean容器

目录 什么是 Springbean 容器 设计思路 图解 参考文章 开源地址 BeanDefinition 类 BeanFactory 类 测试类 什么是 Springbean 容器 Spring 包含并管理应用对象的配置和生命周期,在这个意义上它是一种用于承载对象的容器,你可以配置你的每个 Bea…

AI Agent开发大全第十四课-零售智能导购智能体的RAG开发理论部分

开篇 经过前面的一些课程,我们手上已经积累了各种LLM的API调用、向量库的建立和使用、embedding算法的意义和基本使用。 这已经为我们具备了开发一个基本的问答类RAG的开发必需要素了。下面我们会来讲一个基本问答类场景的RAG,零售中的“智能导购”场景。 智能导购 大家先…

向字符串添加空格

给你一个下标从 0 开始的字符串 s ,以及一个下标从 0 开始的整数数组 spaces 。 数组 spaces 描述原字符串中需要添加空格的下标。每个空格都应该插入到给定索引处的字符值 之前 。 例如,s "EnjoyYourCoffee" 且 spaces [5, 9] &#xff0…

百人会上的蔚小理与「来的刚刚好」的雷军

这就是2025百人会上的蔚小理,努力的李斌、宣扬飞行汽车的何小鹏与大讲开源的李想。那么小米汽车的模式是什么呢?站在蔚小理的肩上。 这就是2025百人会上的蔚小理,努力的李斌、宣扬飞行汽车的何小鹏与大讲开源的李想。那么小米汽车的模式是什么…

解锁Nginx路由器匹配规则

引言 Nginx 无疑是一款备受瞩目的明星产品。它以其高性能、高可靠性以及出色的并发处理能力,在众多 Web 服务器和反向代理服务器中脱颖而出 ,广泛应用于各类网站和应用程序中。据统计,超过 30% 的网站都在使用 Nginx 作为其 Web 服务器&…

传统策略梯度方法的弊端与PPO的改进:稳定性与样本效率的提升

为什么传统策略梯度方法(如REINFORCE算法)在训练过程中存在不稳定性和样本效率低下的问题 1. 传统策略梯度方法的基本公式 传统策略梯度方法的目标是最大化累积奖励的期望值。具体来说,优化目标可以表示为: max ⁡ θ J ( θ )…

Qwt入门

Qwt(Qt Widgets for Technical Applications)是一个用于科学、工程和技术应用的 Qt 控件库,提供曲线图、仪表盘、刻度尺等专业可视化组件。 1. 安装与配置 1.1 安装方式 源码编译(推荐): git clone https://github.com/qwt/qwt.git cd qwt qmake qwt.pro # 生成 Makef…

软考《信息系统运行管理员》- 6.1 信息系统安全概述

信息系统安全的概念 信息系统安全是指保障计算机及其相关设备、设施(含网络)的安全,运行环境的安全, 信息的安全,实现信息系统的正常运行。 信息系统安全包括实体安全、运行安全、信息安全和 人员安全等几个部分。 影响信息系统安全的因素…

Canvas实现旋转太极八卦图

Canvas实现旋转太极八卦图 项目简介 这是一个使用HTML5 Canvas技术实现的动态太极八卦图,包含了旋转动画和鼠标交互功能。项目展示了中国传统文化元素与现代Web技术的结合。 主要特点 动态旋转的太极图八卦符号的完整展示鼠标悬停暂停动画流畅的动画效果 技术实…

机器学习、深度学习和神经网络

机器学习、深度学习和神经网络 术语及相关概念 在深入了解人工智能(AI)的工作原理以及它的各种应用之前,让我们先区分一下与AI密切相关的一些术语和概念:人工智能、机器学习、深度学习和神经网络。这些术语有时会被交替使用&#…

打造高性能中文RAG系统:多轮对话与语义检索的完美结合

目录 1、引言 2、RAG系统的核心架构 3、对话理解:超越单轮问答 3.1、指代消解技术 3.2、话题跟踪与记忆 4、混合检索策略:兼顾精确与广泛 4.1、向量检索 关键词检索 4.2、重排序机制 5、性能优化:应对大规模文档 5.1、向量量化技术…

人工智能助力数字化转型:生成式人工智能(GAI)认证开启新篇章

在数字化浪潮席卷全球的今天,企业正面临着前所未有的转型压力与机遇。数字化转型,这一曾经被视为“选择题”的战略议题,如今已演变为关乎企业生存与发展的“必答题”。在这场深刻的变革中,人工智能(AI)作为…

Windows 图形显示驱动开发-WDDM 2.4功能-GPU 半虚拟化(十二)

DxgkDdiQueryAdapterInfo 更新 DXGKARG_QUERYADAPTERINFO 结构已更新,以包括以下字段以支持半虚拟化: 添加了 Flags 成员,允许 Dxgkrnl 指示以下内容: 它将 VirtualMachineData 设置为指示调用来自 VM。它将 SecureVirtualMach…

iOS审核被拒:Missing privacy manifest 第三方库添加隐私声明文件

问题: iOS提交APP审核被拒,苹果开发者网页显示二进制错误,收到的邮件显示的详细信息如下图: 分析: 从上面信息能看出第三方SDK库必须要包含一个隐私文件,去第三方库更新版本。 几经查询资料得知,苹果在…

马达加斯加企鹅字幕

Antarctica 南极洲 An inhospitable wasteland 一个荒凉的不毛之地 But even here 但即使在这里 on the Earth’s frozen bottom 地球另一端的冰天雪地里 we find life 也有生命存在 And not just any life 不是别的什么生物 Penguins 而是企鹅 Joyous, frolicking 快乐的 顽皮…

爱因斯坦求和 torch

目录 向量点积 矩阵乘法 矩阵转置 向量转换相机坐标系 在 Python 的科学计算库(如 NumPy)中,einsum 是一个强大的函数,它可以简洁地表示各种张量运算。下面是几个不同类型的使用示例: 向量点积 向量点积是两个向量…