详解 Java多线程带来的的风险-线程安全

目录

一、什么是线程安全?

 二、线程不安全的原因

1、线程调度是随机的

2、修改共享数据:多个线程修改同⼀个变量

3、原⼦性  ​编辑

(1)什么是原⼦性

(2)⼀条 java 语句不⼀定是原⼦的,也不⼀定只是⼀条指令 

(3)不保证原⼦性会给多线程带来什么问题

(4)可⻅性:可⻅性指, ⼀个线程对共享变量值的修改,能够及时地被其他线程看到.

(5)Java 内存模型 (JMM) 

 1) 初始情况下, 两个线程的⼯作内存内容⼀致.

​编辑 2) ⼀旦线程1 修改了 a 的值, 此时主内存不⼀定能及时同步. 对应的线程2 的⼯作内存的 a 的值也不⼀定能及时同步.

此时引⼊了两个问题:

1) 为啥整这么多内存?

2) 为啥要这么⿇烦的拷来拷去?

4、指令重排序


引言:

        在 Java 编程中,线程安全是一个至关重要的概念。当多个线程同时访问共享的资源时,如果没有适当的同步措施,就会出现数据竞争和不一致的情况,从而导致程序出现各种难以预料的错误。因此,理解并处理线程安全问题是编写高质量、可靠性强的 Java 程序的关键。

一、什么是线程安全?

        线程安全是指在多线程环境中,对共享资源的访问不会导致数据的损坏或不一致。一个线程安全的程序在多线程环境下执行时,能够确保各个线程都能正确地操作共享的数据,而不会产生意外结果。

 二、线程不安全的原因

1、线程调度是随机的

这是线程安全问题的 罪魁祸⾸
随机调度使⼀个程序在多线程环境下, 执⾏顺序存在很多的变数.
程序猿必须保证 在任意执⾏顺序下 , 代码都能正常⼯作.

2、修改共享数据:多个线程修改同⼀个变量

3、原⼦性  

(1)什么是原⼦性
我们把⼀段代码想象成⼀个房间,每个线程就是要进⼊这个房间的⼈。如果没有任何机制保证,A进⼊ 房间之后,还没有出来;B 是不是也可以进⼊房间,打断 A 在房间⾥的隐私。这个就是不具备原⼦性 的。
那我们应该如何解决这个问题呢?是不是只要给房间加⼀把锁,A 进去就把⻔锁上,其他⼈是不是就进不来了。这样就保证了这段代码的原⼦性了。
有时也把这个现象叫做同步互斥,表⽰操作是互相排斥的。
(2)⼀条 java 语句不⼀定是原⼦的,也不⼀定只是⼀条指令 
⽐如刚才我们看到的 n++,其实是由三步操作组成的:
  1. 从内存把数据读到 CPU
  2. 进⾏数据更新
  3. 把数据写回到 CPU
(3)不保证原⼦性会给多线程带来什么问题
如果⼀个线程正在对⼀个变量操作,中途其他线程插⼊进来了,如果这个操作被打断了,结果就可能是错误的。 这点也和线程的抢占式调度密切相关. 如果线程不是 "抢占" 的, 就算没有原⼦性, 也问题不⼤.
(4)可⻅性:可⻅性指, ⼀个线程对共享变量值的修改,能够及时地被其他线程看到.
(5)Java 内存模型 (JMM) 
Java虚拟机规范中定义了Java内存模型.
⽬的是屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台下都能达到⼀致的并发效果.

 

  • 线程之间的共享变量存在 主内存 (Main Memory).
  • 每⼀个线程都有⾃⼰的 "⼯作内存" (Working Memory) .
  • 当线程要读取⼀个共享变量的时候, 会先把变量从主内存拷⻉到⼯作内存, 再从⼯作内存读取数据
  • 当线程要修改⼀个共享变量的时候, 也会先修改⼯作内存中的副本, 再同步回主内存.  
由于每个线程有⾃⼰的⼯作内存, 这些⼯作内存中的内容相当于同⼀个共享变量的 "副本". 此时修改线程1 的⼯作内存中的值, 线程2 的⼯作内存不⼀定会及时变化
 1) 初始情况下, 两个线程的⼯作内存内容⼀致.
 2) ⼀旦线程1 修改了 a 的值, 此时主内存不⼀定能及时同步. 对应的线程2 的⼯作内存的 a 的值也不⼀定能及时同步.

 这个时候代码中就容易出现问题.

此时引⼊了两个问题:
  • 为啥要整这么多内存?
  • 为啥要这么⿇烦的拷来拷去?
1) 为啥整这么多内存?
实际并没有这么多 "内存". 这只是 Java 规范中的⼀个术语, 是属于 "抽象" 的叫法.
所谓的 "主内存" 才是真正硬件⻆度的 "内存". ⽽所谓的 "⼯作内存", 则是指 CPU 的寄存器和⾼速缓存.
2) 为啥要这么⿇烦的拷来拷去?
因为 CPU 访问⾃⾝寄存器的速度以及⾼速缓存的速度, 远远超过访问内存的速度(快了 3 - 4 个数量级, 也就是⼏千倍, 上万倍).
⽐如某个代码中要连续 10 次读取某个变量的值, 如果 10 次都从内存读, 速度是很慢的. 但是如果只是 第⼀次从内存读, 读到的结果缓存到 CPU 的某个寄存器中, 那么后 9 次读数据就不必直接访问内存了. 效率就⼤⼤提⾼了.

 那么接下来问题⼜来了, 既然访问寄存器速度这么快, 还要内存⼲啥??

答案就是⼀个字: 贵

 值的⼀提的是, 快和慢都是相对的. CPU 访问寄存器速度远远快于内存, 但是内存的访问速度⼜远远快 于硬盘. 对应的, CPU 的价格最贵, 内存次之, 硬盘最便宜

 

4、指令重排序

什么是代码重排序
⼀段代码是这样的:
  1. 去前台取下 U 盘
  2. 去教室写 10 分钟作业
  3. 去前台取下快递
如果是在单线程情况下,JVM、CPU指令集会对其进⾏优化,⽐如,按 1->3->2的⽅式执⾏,也是没问题,可以少跑⼀次前台。这种叫做指令重排序

 编译器对于指令重排序的前提是 "保持逻辑不发⽣变化". 这⼀点在单线程环境下⽐较容易判断, 但是在多线程环境下就没那么容易了, 多线程的代码执⾏复杂程度更⾼, 编译器很难在编译阶段对代码的执⾏效果进⾏预测, 因此激进的重排序很容易导致优化后的逻辑和之前不等价.

 总结:

        在编写 Java 程序时,必须时刻牢记线程安全问题,合理使用同步机制、并发容器和原子类等工具,确保多线程环境下程序的正确性和稳定性。只有这样,才能编写出高效、健壮的多线程应用。

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

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

相关文章

文心一言指令词宝典之求职招聘篇

作者:哈哥撩编程(视频号、抖音、公众号同名) 新星计划全栈领域优秀创作者博客专家全国博客之星第四名超级个体COC上海社区主理人特约讲师谷歌亚马逊演讲嘉宾科技博主极星会首批签约作者 🏆 推荐专栏: 🏅…

C语言函数递归调用

在C语言中,函数可以直接或间接地调用自身,这种函数调用自身的过程称为递归调用。递归是一种强大的编程技巧,能够简化程序结构、提高代码的可读性和可维护性。本文将介绍C语言函数递归调用的原理、应用场景以及注意事项。 以下是我整理的关于…

PinkysPalaceV2靶场详解IDA逆向查看缓存区溢出漏洞原理以及使用kali gdb使用超详细三次提权字典生成

下载链接: Pinkys Palace: v2 ~ VulnHub 安装: 正常用vm虚拟机打开即可,注意导入时所选择的硬盘存储目录应为空目录,否则会导入失败 根据下载链接提示我们需要更改host文件,以便于我们可以正常访问 kali中的host文件位置为 /etc/h…

理解虚拟dom

什么是虚拟dom? 虚拟 DOM是由一系列的 JavaScript 对象组成的树状结构,每个对象代表着一个DOM元素,包括元素的标签名、属性、子节点等信息。虚拟 DOM中的每个节点都是一个 JavaScript 对象,它们可以轻松地被创建、更新和销毁&…

弧形导轨在自动化设备中的传动原理

在自动化机械系统中,弧形导轨是一种常见的轨道结构,用于支撑和引导物体沿着指定的弧线运动。其工作原理基于几何学和物理学的原理。 弧形导轨通常由一个弧形的轨道和一个移动部件组成。轨道一般呈弧形,其几何形状可以是圆弧、椭圆弧等&#x…

Java(2)之变量,数据类型,运算符

1、标识符 在java中标识符是由字母数字下划线,美元符号组成(其中数字没办法作首)在java中是区分大小写的。 2、关键字 关键字是在java中赋予了特殊的含义的字符,只能用于特定地方 例如包package 类class 接口interface 引用 &a…

智慧工地整体解决方案(3)

塔吊安全管理系统 需求分析 塔式起重机是现代施工中必不可少的关键设备,是施工企业装备水平的标志性重要装备之一。随着近年来建筑行业塔机的大量使用,由于塔机违规超限作业和塔机群干涉碰撞等引发的各类塔机运行安全事故频繁发生,造成了巨大的生命财产损失。 典型事故现…

Linux 设备树: 设备树节点与属性在 dtb 文件中的存储

前言 当前新版本的 Linux 内核 设备驱动框架,与设备树(Device Tree)结合密切,整体 设备树的设备驱动框架,比较的庞大,但又非常的经典。 一个个的 设备树解析函数,都是前人【智慧】的结晶&#…

【Golang星辰图】解密数据序列化:探索Go语言中的数据序列化库,提升开发效率

提升效率:了解Go语言中的高效数据序列化解决方案 前言 Go语言作为一门流行的编程语言,其生态系统中涵盖了许多强大且高效的数据序列化库。本文将深入探索Go语言的几个著名数据序列化库,包括go-msgpack、go-bson、go-base58、go-json、go-xm…

【pysurvival Python 安装失败】

这个错误与 sklearn 包的名称更改有关,导致 pysurvival 在构建元数据时失败。现在,你需要修改 pysurvival 的安装文件以使用正确的 scikit-learn 包名 编辑安装文件:找到 pysurvival 的安装文件,可能是 setup.py 或 pyproject.to…

2024系统架构师---隐式调用架构风格的概念与应用

隐式调用架构风格,也被称为事件驱动架构,是一种软件架构模式,其中组件间的交互是基于事件的,而不是直接的调用。这种架构风格侧重于系统中的事件生成、事件监听和事件处理,使组件间的耦合度降低,提高了模块…

智慧公厕:改变城市生活的革命性基础设施

在现代城市的高品质生活要求背景下,公共厕所成为了不可或缺的基础设施。然而,传统的公厕在服务质量、管理效率方面存在一定的问题。为了提供更好的公厕服务,智慧公厕应运而生。通过智能化的管理模式,智慧公厕实现了公厕使用与管理…

玩转ChatGPT:Suno制作音乐

AI开始进军音乐领域了。 一款音乐AI神器——Suno V3发布,它能够处理从间奏到主歌、副歌、桥段直至尾奏的完整结构,零门槛创作音乐。 需要科学上网,官方网站:https://app.suno.ai/ 使用GPT写个歌词,然后丢进Suno生成…

Web APIs知识点讲解(阶段七)

正则表达式 1.能够利用正则表达式校验输入信息的合法性2. 具备利用正则表达式验证小兔鲜注册页面表单的能力 一.正则表达式 1.正则表达式 正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也…

关于yolov8x6.yaml的一些理解

下面是yaml的内容: # Ultralytics YOLO 🚀, AGPL-3.0 license # YOLOv8 object detection model with P3-P6 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect# Parameters nc: 80 # number of classes scales: # model compound scaling const…

光明源@智慧厕所公厕软件系统有哪些核心功能?

在现代城市的建设中,智慧公厕的建设成为了提升城市品质和居民生活质量的重要举措。而智慧公厕的核心,不仅仅在于其硬件设备的智能化,同样重要的是其背后支持的智慧厕所公厕软件系统。让我们一起探讨,智慧厕所公厕软件系统有哪些核…

Spring面试题系列-6

Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。 Spring中的单…

ESP32烧录固件(二种方法)

在选择不同固件时可根据我们的需要选择合适的固件,还需根据开发板Flash的内存大小选择,查看Flash的内存大小,可查阅下面的第二种方法中查看开发板信息。 官方固件 MicroPython - 用于微控制器的Python https://micropython.org/download/ESP…

主函数的三个参数

1).主函数的三个参数的含义: argc:主函数的参数个数 argv:主函数的参数内容 envp:环境变量; 2).演示代码: #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc,char *argv[],char *envp[]) { int i0;printf("argc%d\n&…

API接口开发1688阿里巴巴官方API接口获得店铺详情1688.seller_info-API接入演示

要获取店铺详情&#xff0c;你需要使用1688API接口。以下是一个简单的Python示例&#xff0c;展示了如何使用requests库调用阿里巴巴API并解析返回的数据&#xff1a; # coding:utf-8 """ Compatible for python2.x and python3.x requirement: pip install re…