线程不安全的原因2

news/2024/5/7 8:04:32/文章来源:https://blog.csdn.net/m0_62160964/article/details/127182779

多线程系列文章目录


在这里插入图片描述

💥 💥 💥如果你觉得我的文章有帮助到你,还请【关注➕点赞➕收藏】,得到你们支持就是我最大的动力!!!
💥 💥 💥

版权声明:本文由【马上回来了】原创、在CSDN首发、需要转载请联系博主。
版权声明:本文为CSDN博主「马上回来了」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

在这里插入图片描述

🚀🚀🚀 新的知识开始喽🚀🚀🚀
在这里插入图片描述

文章目录

  • 多线程系列文章目录
  • 0.常见的线程不安全的原因
  • 2.操作系统的随机抢占式调度
  • 3.操作语句不是原子的
  • 4.编译器优化所带来的内存可见性问题
  • 5.编译器优化带来的指令重排序的问题


0.常见的线程不安全的原因

上篇文章线程不安全的原因1已经详细的讲解了线程不安全的原因之一就是没有上锁,这篇文章继续讲解剩下的常见的导致多线程不安全的原因:
1.操作系统的随机抢占式调度
2.多个线程同时修改同一个变量(这个如果是业务逻辑的需要,就无法避免,只能根据剩下的几个原因来保证多线程修改同个变量是线程安全的)
3.操作语句不是原子的
4.编译器优化带来的内存可见性问题
5.编译器优化带来的指令重排序的问题

2.操作系统的随机抢占式调度

这是多线程不安全的万恶之源,操作系统对于多个线程的调度顺序是随机的,没有固定的顺序,当程序运行起来之后每个线程都去抢占式去占用cpu,而线程的执行顺序在很多业务逻辑里是非常重要的,因此为了缓解这个问题,java的标准库里提供了wait和notify两个方法.
wait是Object类里的方法,所以任何对象都能调用这个方法,wait方法的作用是让调用这个方法的线程进入阻塞状态.
在这里插入图片描述
常用的是wait不带参数的方法和带有一个时间参数的方法:
在这里插入图片描述
notify是和wait配套使用的,notify能将线程里由于wait导致线程阻塞的线程给唤醒:
在这里插入图片描述
wait和notify的使用要注意:
在这里插入图片描述
在这里插入图片描述

看代码:

public class demo1 {public static void main(String[] args) {Object object = new Object();Thread t = new Thread(){@Overridepublic void run() {synchronized (object){System.out.println("wait前的业务逻辑");try {object.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println("wait后的逻辑");}}};Thread t1 = new Thread(){@Overridepublic void run() {synchronized (object){System.out.println("notify之前的逻辑");object.notify();System.out.println("notify之后的逻辑");}}};t.start();t1.start();}
}

在这里插入图片描述

运行结果:
在这里插入图片描述
wait和notify的更多使用案例请看我的文章多线程案例2:阻塞队列的模拟实现,

3.操作语句不是原子的

就是要加锁,上篇文章讲的非常详细线程不安全的原因1:synchronized

4.编译器优化所带来的内存可见性问题

先看这个代码:
在这里插入图片描述
在这里插入图片描述

class Count{public int count = 0;
}
public class demo1 {public static void main(String[] args) {Count count = new Count();Thread t = new Thread(){@Overridepublic void run() {while (count.count == 0){}System.out.println("t线程结束");}};t.start();Thread t1 = new Thread(){@Overridepublic void run() {System.out.println("请输入数字:");Scanner scanner = new Scanner(System.in);count.count = scanner.nextInt();}};t1.start();}}

运行结果:
在这里插入图片描述
因此要解决这个潜在的问题,可以使用关键字volatile,用volatile修饰count,就可以阻止编译器只访问一次内存,保证load每次都是从内存上读取数据:
在这里插入图片描述

class Count{volatile public int count = 0;
}
public class demo1 {public static void main(String[] args) {Count count = new Count();Thread t = new Thread(){@Overridepublic void run() {while (count.count == 0){}System.out.println("t线程结束");}};t.start();Thread t1 = new Thread(){@Overridepublic void run() {System.out.println("请输入数字:");Scanner scanner = new Scanner(System.in);count.count = scanner.nextInt();}};t1.start();}}

运行结果:
在这里插入图片描述
如果让while不转那么快,编译器就不会触发优化:
在这里插入图片描述

class Count{public int count = 0;
}
public class demo1 {public static void main(String[] args) {Count count = new Count();Thread t = new Thread(){@Overridepublic void run() {while (count.count == 0){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("t线程结束");}};t.start();Thread t1 = new Thread(){@Overridepublic void run() {System.out.println("请输入数字:");Scanner scanner = new Scanner(System.in);count.count = scanner.nextInt();}};t1.start();}}

运行结果:
在这里插入图片描述
或者可以让其他线程快速修改变量,使while循环里第一次load就能及时获取到已经更新好的count的数值:
在这里插入图片描述

 class Count0{public int count = 0;
}
public class demo2 {public static void main(String[] args) {Count0 count = new Count0();Thread t = new Thread() {@Overridepublic void run() {while (count.count == 0) {}System.out.println("t线程结束");}};t.start();Thread t1 = new Thread(){@Overridepublic void run() {count.count = 1;}};t1.start();}}

内存可见性问题对应JMM模型(Java Memory Model).
volatile防止编译器进行内存可见性的优化,避免直接从CPU寄存器上读取数据,保证每次都是从内存上读取数据.
站在JMM模型来看待volatile:
正常的程序运行时,会把主内存(内存)的数据先加载到工作内存(CPU的寄存器/缓存区)中,在进行计算处理.
编译器优化可能导致不是每次都读取主内存,而是直接读取工作内存中的缓存数据,因此就可能到导致内存可见性的问题,volatile的效果就是保证每次都是从主内存里读取数据.

5.编译器优化带来的指令重排序的问题

这在我的文章多线程案例1:单例模式的第四点里有详细讲解:
在这里插入图片描述



🌏🌏🌏今天的你看懂这里又学到了很多东西吧🌏🌏🌏

在这里插入图片描述

🌔 🌔 🌔下次见喽🌔 🌔 🌔
在这里插入图片描述

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

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

相关文章

C语言---14文件操作---03文件加密器

一、原理分析 对称加密体制是传统而经典的加密体制策略 加密方A使用该密钥key对要保密的文件进行加密操作,从而生成密文 解密方B同样使用该密钥key对加密文件实施解密操作,从而生成明文 (一)加密过程 (二&#xff09…

【Ubuntu启动菜单的默认项】

【Ubuntu启动菜单的默认项】1. 摘要2. 具体实现2.1 打开grub配置2.2 修改默认启动4. 总结欢迎大家阅读2345VOR的博客【Ubuntu同步系统时间】🥳🥳🥳 本人主页: 已获得CSDN《嵌入式领域新星创作者》称号👻👻&…

【PyTorch深度学习项目实战100例】—— 使用GRU进行天气变化的时间序列预测 | 第11例

前言 大家好,我是阿光。 本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmPy…

Python-入门-模块和包(十三)

文章目录Python-入门-模块和包导入模块自定义模块自定义模块编写说明文档导入文件名中带空格与文件名以数字开头的模块\_\_name\_\_\_\_main\_\_作用详解导入模块的3种扩展方式动态执行模块模块内成员私有 多模块管理(包的概念)查看模块(变量、函数、类)…

上手全局锁,死锁

全局锁 就是对 整个数据库实例 加锁。当你需要让整个库处于 只读状态 的时候,可以使用这个命令,之后 其他线程的以下语句会被阻塞:数据更新语句(数据的增删改)、数据定义语句(包括建表、修改表结 构等&…

《uni-app》一个非canvas的飞机对战小游戏实现(一)准备

这是一个没有套路的前端博主,热衷各种前端向的骚操作,经常想到哪就写到哪,如果有感兴趣的技术和前端效果可以留言~博主看到后会去代替大家踩坑的~接下来的几篇都是uni-app的小实战,有助于我们更好的去学习u…

【论文阅读】Multitask Prompted Training Enables Zero-shot Task Generalization

文章目录AbstractIntroductionMeasuring generalization to held-out tasksA unfied prompt formatExperimental setupResultsConclusionAbstract 大模型在多种任务上学习提高了 zero-shot 泛化能力,有人假设这是语言模型在隐式多任务学习的结果。 zero-shot 泛化…

【C++】类和对象(上篇)——类的定义,访问限定符与this指针

前言 C 语言和 C 最大的区别就是一个面向过程,一个面向对象。而提到面向对象就不得部提到类,这一篇文章,我们主要探讨一下 C 中类的定义以及一些基本的权限。 目录 一、类的引入 二、类的定义 三、访问限定符 3.1 public 3.2 private / …

PTA - 数据库合集11

目录 10-74 修改学生选课成绩 10-75 添加成绩等级rank字段 10-77 删除成绩为空的学生选课记录 10-74 修改学生选课成绩 分数 5 全屏浏览题目 切换布局 作者 张庆 单位 集美大学 本题目要求编写UPDATE语句, 在SC表中修改‘C001’课程的成绩,若成绩小…

【自学】利用python进行数据分析 LESSON6 <pandas入门——pandas数据结构介绍2>

目录 前言 一、DataFrame 1. 列的选取 2. 行的选取 3. 列的修改 4. 列的删除 5. 嵌套字典赋给DataFrame 总结 前言 继续上一节的内容。往期内容如下: 【自学】利用python进行数据分析 LESSON5 <pandas入门——pandas数据结构介绍1>_…

数据结构初阶 顺序表补充

一. 题目的要求 写出三种链表的接口函数 它们的功能分别是 1 查找数的位置 2 在pos位置插入值 3 在pos位置删除值 二. 实现pos 这个其实很简单 找到一步步遍历 找到这个数字就返回 找不到就提示用户下 这 个数字不存在 int SeqListFind(SL* ps,int x) {int i;for ( i …

Red Hat Enterprise Linux release 8.0 (Ootpa)-性能调优工具Tuned

一、Tuned简介Tuned是Red Hat Linux操作系统自带的性能调优工具,通过针对特定应用场景提供配置来改善系统性能,自Red Hat Enterprise Linux/CentOS的6.3版本开始出现,包括两部分tuned和tuned-adm,其中tuned是服务端程序,用来监控和收集系统各个组件的数据,并依据数据提供…

SpringBoot二十六课大纲和目录

目录 ​​ 即使是在憎恨和杀戮中,仍然有些东西值得人们为之活下去。一次美丽的相遇,或是为了美丽事物的存在。我们描绘憎恨,是为了描写更重要的东西。我们描绘诅咒,是为了描写解放后的喜悦。 SpringBoot《第一课》_星辰镜的博客…

clickhouse常见部署图及LowCardinality低基数类型优缺点

clickhouse使用jdbc进行查询插入数据操作的部署图 一.读sql的核心是 读分布式表的数据(分布式表可以读取互为副本的本地表的数据,起到容灾的目的),然后使用nginx作为负载均衡器和反向代理,代理后端clickhouse的分布式…

什么是低代码

文章目录no code / low code / pro code按适用范围的维度来分类低代码的技术意义与商业价值技术意义商业价值行业状态速读平台分类不同的实现方式不同的使用群体不同的使用方式优秀开源项目推荐no code / low code / pro code no code:自己编程给自己用&#xff0c…

Yao‘s GC 的通信最优解:Half Gate

参考文献: Bellare M, Hoang V T, Rogaway P. Foundations of garbled circuits[C]//Proceedings of the 2012 ACM conference on Computer and communications security. 2012: 784-796.Zahur S, Rosulek M, Evans D. Two halves make a whole[C]//Annual Interna…

MyBatisPlus入门宝典(二)CRUD

目录 一.添加 二.相关注解 三.修改 四.删除 五.查询 六.条件构造器 七.分页查询 八.全局配置 一.添加 1.配置文件开启SQL日志打印 # 开启SQL日志 mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl 2.测试添加方法: …

Unity URP 色彩之旅

Unity URP 色彩之旅 这一切只是色彩科学的冰山一角… 文章目录Unity URP 色彩之旅1 我们是如何感知世界的?1.1 首先要有光!1.2 人眼响应1.3 奇怪的大脑2 我们是如何描述颜色的?2.1 CIE 1931 RGB Color Space2.2 CIE 1931 XYZ Color Space2.3 …

JavaScript高级学习笔记:数据_变量_内存

1. 什么是数据? 2. 什么是内存? 3. 什么是变量? 4. 内存,数据, 变量三者之间的关系 变量保存的是内存中存储的地址值,而变量赋值就是将一个变量保存的内容拷贝一份到另一个变量中 这里面的.就是找obj对应地址值,中内存保存的相应数据 那么是不是所有…

SRv6----报文转发流程

按照下图路径,报文需要从主机H1转发到主机H2,H1将报文发送给节点A处理。节点A、B、D和F均为支持SRv6的设备,节点C和节点E为不支持SRv6的设备。 我们在SRv6源节点A上进行了网络编程,希望报文经过B-C和D-E这两条链路,然后送达节点F&…