线程安全之synchronized和volatile
目录
1.线程不安全的原因
2.synchronized和volatile
2.1 synchronized
2.1.1 synchornized的特性
2.1.2 synchronized使用示例
2.2 volatile
我们先来看一段代码:

分析以上代码,t1和t2这两个线程的任务都是分别将count这个变量自增5000次,最后由主线程将count的值输出。很显然,t1和t2这两个线程都对count这个变量自增5000次的话,那最终的count的值就是10000.我们运行于一下代码,看是否与预期相同:

为什么不是10000呢,这就与我们的线程安全有关了。
这段代码的逻辑很简单,但是忽略了线程的安全性问题。以上代码涉及到多个线程对同一个变量counter.count的修改.我们要知道对一个变量进行修改一般有三条指令:
- 变量从内存中读到某个寄存器上
- 对变量进行操作
- 将变量从寄存器读回到内存中
那么以上代码就可以用如下图来表示其运行时的某种状态:
因为操作系统的随机调度,导致t1线程可能刚把count=0读取到寄存器上还没开始修改写回时,t2线程就被操作系统安排上了cpu,导致读取到的数据也是count=0,最后当两个线程将其数据分别写回后,内存中count的值就为1.这也就是为什么那段代码的运行结果与预期不符.
1.线程不安全的原因
- 操作系统的随机调度:这个是造成线程不安全的最根本原因,我们人为也不好去解决.
- 多线程同时修改同一个变量:多个线程针对同一个线程进行修改,例如以上代码.
- 修改操作不是原子性的:所谓原子操作是指不会被线程调度机制打断的操作.以上代码t1和t2线程对变量的修改操作就不是原子的,例如t1线程还没有将数据修改完写回内存,t2线程就开始读取、修改。
- 内存不可见:可见性是指一个线程对共享变量值修改后,其他线程能够从内存中去读取而不是从当前CPU的寄存器或高速缓存中读取。反之不可见就是内存中的数据可能已经被某个线程改了,但某些线程仍然在寄存器或高速缓存中读取未修改之前的数据。这样就会导致线程不安全。
内存可见性分析:
- 线程之间的共享变量存在内存中。然后每个线程都有一个自己的“工作内存”(也就是寄存器 和高速缓存)。
- 当某个线程要读取一个共享变量时,会先把变量从内存中拷贝到自己的“工作内存”中,然后从“工作内存”中读取数据;
- 当某个线程要修改某个变量时,会直接从自己的“工作内存”去读取之前拷贝的副本,修改后在同步给内存。这样直接在自己的“工作内存”中访问数据虽然可以加快读取速度,但是却存在读错数据的风险,因为无法确定现在线程自己的“工作内存”中的数据是否和主内存中的数据一致.有可能主内存的数据已经被其他线程改了。
所以我们经常说线程安全的可见性是指:一个线程对共享变量值修改后,其他线程能够及时发现,然后从内存中去读取数据,而不是从当前CPU的寄存器或高速缓存中读取。
- 指令重排序:这里的"序"是指CPU中指令执行的顺序,也就是一条条汇编指令执行的顺序。指令重排序是指CPU会在执行指令前进行优化,即对指令顺序做了调整.指令的重排序也会导致线程不安全。
编译器对指令重排序的前提是”保持逻辑不发生变化“,这一点在单线程下很容易判断。但是在多线程下就很难了。多线程的代码执行复杂程度高,编译器很难在编译阶段对代码的执行效果进行预测,因此很容易导致优化后的逻辑和之前不等价.
有序性涉及到CPU以及编译器的一些底层工作原理,就不做过多解释了。
2.synchronized和volatile
2.1 synchronized
synchornized会起到互斥的效果,某个线程执行到某个对象的synchronized中时,其它线程如果也执行到这个对象的synchronized时,就会阻塞等待。
- 进入synchronized修饰的代码块,相当于对这段代码进行加锁;
- 退出synchronized 修饰的代码块,相当于对这段代码进行解锁。
就是当t1线程先调用这个被synchronized修饰的方法时,该线程就会获得这把锁,然后进行读取内存,自增,写回内存这些操作。线程走完了这段代码才会将锁打开。而其他线程在t1这个线程获得锁的期间中,不能访问这段代码,只能阻塞等待,直到t1线程释放锁,才能在操作系统的分配下再去竞争这把锁。也就是说,当一个线程获得锁之后,其他线程再想获得这把锁只能阻塞等待,直到这个线程将锁释放。
这时再运行代码时,结果就如预期了:

此期间synchornized的工作过程如下:
- 获得互斥锁
- 从内存中读取数据副本
- 将数据进行修改
- 将修改后的数据写入内存
- 释放互斥锁
2.1.1 synchornized的特性
- 互斥:synchornized会起到互斥的效果,某个线程执行到某个对象的synchronized中时,其它线程如果也执行到这个对象的synchronized时,就会阻塞等待。
- 可重入:可重入锁的内部,包含了 "线程持有者" 和 "计数器" 两个信息。
可重入:
若某个线程加锁的时候,发现锁已经被占用,而占用者恰好是自己,那么仍然可以继续获取到锁,并让计数器自增。解锁的时候计数器递减为0,释放锁。
- synchronized可以保证线程的原子性,使线程更安全。
2.1.2 synchronized使用示例
1).修饰普通方法:对Demo对象加锁

2).修饰静态方法:对Demo类对象加锁

3).修饰代码块:明确指定锁哪个对象,锁当前对象

4).锁类对象

我们始终要明白,synchronized锁的究竟是什么。只有当多个线程竞争同一把锁时,才会产生阻塞等待。而多个线程竞争多个不同的锁时,不会发生竞争。
2.2 volatile
我们说线程的安全性有原子性,可见性,有序性。volatile这个关键字就可以实现线程安全的可见性。上面说了,CPU在读取内存中的数据时,会将数据先读到自己的“工作内存(CPU的寄存器和高速缓存)”上,然后CPU直接与“工作内存”上的数据打交道。但是如果频繁的读取内存,频繁的从内存上读到“工作内存”上的数据都相同的话,那么CPU为了优化速率,可能就不会每次都从内存开始读起,而是直接在“工作内存”上读取。因为直接读寄存器会比读内存要快很多。但是这样就容易导致如果其他线程改了内存上的数据,该线程不能及时知道,还在读寄存器或高速缓存。而volatile关键字就能保证让CPU每次都去读内存。这样就能及时直到内存上去读取数据,但是每次都从内存上去读取数据的话,会导致效率大大降低。
现在我们通过以下代码来了解一下volatile关键字的用法及作用:
分析以上代码,我们不难看出,我们可以通过在t2线程中输一入个非0整数来改变Test.flag的值,从而使t1线程结束循环。我们来运行一下代码:
显然,当我们输入一个非0整数后,程序还没结束运行,说明t1线程还没结束循环。为什么呢,很简单,这就涉及到了之前说过的内存可见性问题。因为在输入非0整数时,t1线程可能已经循环了千万次,每一次循环从内存上读到的Test.flag都不变,于是CPU就进行优化,不再每次都从内存中读取了,而是直接从寄存器或高数缓存上去读取数据。但是之后如果其它线程将内存上的Test.flag修改之后,当前线程还不知道,还在一直读寄存器或高数缓存的数据,这样就会导致循环不能及时停止。所以就出现了以上代码的情况。而我们用volatile关键字对Test.flag这个变量修饰后,就能强制让CPU每次读取这个数据时,都是先从内存中去读取,这样可以达到了内存可见性的要求。修改代码如下:
再来运行一下:
这样就达到预期了。
总结:
- synchronized的作用是加锁,保证线程安全特性中的原子性;
- volatile的作用是强制CPU读数据时从内存上开始读取,保证的是可见性。
相关文章:
线程安全之synchronized和volatile
目录 1.线程不安全的原因 2.synchronized和volatile 2.1 synchronized 2.1.1 synchornized的特性 2.1.2 synchronized使用示例 2.2 volatile 我们先来看一段代码: 分析以上代码,t1和t2这两个线程的任务都是分别将count这个变量自增5000次ÿ…...
量子计算对网络安全的影响
量子计算的快速发展,例如 IBM 的 Quantum Condor 处理器具有 1000 个量子比特的容量,促使专家们宣称第四次工业革命即将实现“量子飞跃”。 量子计算机的指数处理能力已经受到政府和企业的欢迎。 由于从学术和物理原理到商业可用解决方案的不断转变&am…...
MyBatis——增删改查操作的实现
开启mybatis sql日志打印 可以在日志中看到sql中执行的语句 在配置文件中加上下面这几条语句 mybatis.configuration.log-implorg.apache.ibatis.logging.stdout.StdOutImpl logging.level.com.example.demodebug查询操作 根据用户id查询用户 UserMapper: User…...
【7】linux命令每日分享——cat查看文件内容
大家好,这里是sdust-vrlab,Linux是一种免费使用和自由传播的类UNIX操作系统,Linux的基本思想有两点:一切都是文件;每个文件都有确定的用途;linux涉及到IT行业的方方面面,在我们日常的学习中&…...
新氧2023年财务业绩预测:退市风险大幅降低,收入增长将放缓
来源:猛兽财经 作者:猛兽财经 公司进展 与新氧(SY)有关的两个重要积极进展值得一提。 第一个积极进展是新氧的退市风险已在很大程度上降低。 2023年1月6日,新氧披露,它已经“重新符合纳斯达克规定的股价每…...
C++使用shared_ptr与weak_ptr轻松管理内存
智能指针之shared_ptr与weak_ptr前言智能指针实例分析前言 C与其他语言的不同点之一就是可以直接操作内存,这是一把双刃剑,直接操作内存可以提高开发的灵活度,开发人员在合适的时机申请内存,在合适的时机释放内存,减少…...
Buuctf reverse [FlareOn4]IgniteMe 题解
一. 查壳 无壳32位程序 二. ida打开 GetStdHandle函数根据微软官方文档可以得知是获取标准输入/输出/错误的句柄 参数里的 0xFFFFFFF6转换一下是4294967286, 对应(DWORD) -10 所以这里的WriteFile函数实际上是实现了printf的功能 sub_4010F0()函数 其功能是通过ReadFile函数读取…...
ChatGPT懂SAP吗?跟ChatGPT聊完后,我暂时不担心会失业
我弄了个ChatGPT账号。随便问了一下他SAP的问题。贴出来跟大家分享一下。 问题1:我是 SAP 开发工程师,你有什么可以帮到我。 作为一个语言模型,我可以为您提供关于SAP开发的一般性建议,以及关于如何使用SAP软件的一些技巧。以下是…...
Communications link failure 解决方法 !!!
Communications link failure 解决方法 最近在使用mybatis逆向生成的时候遇到了link failure的问题, 这里记录解决方法 文章目录Communications link failure 解决方法generatorConfig 里的数据库链接或数据库或密码或账户填写错误mysql响应时间设置太短, 还没等链接上就关闭连…...
pytorch入门2--数据预处理、线性代数的矩阵实现、求导
数据预处理是指将原始数据读取进来使得能用机器学习的方法进行处理。 首先介绍csv文件: CSV 代表逗号分隔值(comma-separated values),CSV 文件就是使用逗号分隔数据的文本文件。 一个 CSV 文件包含一行或多行数据,每一…...
15.消息队列RabbitMQ
一、基本概念 RabbitMQ 是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息…...
并发编程之死锁问题介绍
一、本文概览 死锁问题在并发编程中是一个非常致命的问题,问题一旦产生,只能通过重启机器、修改代码来修复问题,下面我们通过一小段文章内容介绍下死锁以及如何死锁的预防 二、什么是死锁? 在介绍死锁之前,先来明确下什…...
【python学习笔记】:SQL常用脚本(一)
1、行转列的用法PIVOT CREATE table test (id int,name nvarchar(20),quarter int,number int) insert into test values(1,N苹果,1,1000) insert into test values(1,N苹果,2,2000) insert into test values(1,N苹果,3,4000) insert into test values(1,N苹果,4,5000) insert…...
Spring是怎么解决循环依赖的
1.什么是循环依赖: 这里给大家举个简单的例子,相信看了上一篇文章大家都知道了解了spring的生命周期创建流程。那么在Spring在生命周期的哪一步会出现循环依赖呢? 第一阶段:实例化阶段 Instantiation 第二阶段:属性赋…...
HTML创意动画代码
目录1、动态气泡背景2、创意文字3、旋转立方体1、动态气泡背景 <!DOCTYPE html> <html> <head><title>Bubble Background</title><style>body {margin: 0;padding: 0;height: 100vh;background: #222;display: flex;flex-direction: colum…...
软工第一次个人作业——阅读和提问
软工第一次个人作业——阅读和提问 项目内容这个作业属于哪个课程2023北航敏捷软件工程这个作业的要求在哪里个人作业-阅读和提问我在这个课程的目标是体验敏捷开发过程,掌握一些开发技能,为进一步发展作铺垫这个作业在哪个具体方面帮助我实现目标对本课…...
urho3d的自定义文件格式
Urho3D尽可能使用现有文件格式,仅在绝对必要时才定义自定义文件格式。当前使用的自定义文件格式有: 二进制模型格式(.mdl) Model geometry and vertex morph data byte[4] Identifier "UMDL" or "UMD2" …...
spark第一章:环境安装
系列文章目录 spark第一章:环境安装 文章目录系列文章目录前言一、文件准备1.文件上传2.文件解压3.修改配置4.启动环境二、历史服务器1.修改配置2.启动历史服务器总结前言 spark在大数据环境的重要程度就不必细说了,直接开始吧。 一、文件准备 1.文件…...
MySQL---存储过程与存储函数的相关概念
MySQL—存储过程与存储函数的相关概念 存储函数和存储过程的主要区别: 存储函数一定会有返回值的存储过程不一定有返回值 存储过程和函数能后将复杂的SQL逻辑封装在一起,应用程序无需关注存储过程和函数内部复杂的SQL逻辑,而只需要简单地调…...
PMP值得考吗?
第一,PMP的价值体现 1、PMP是管理岗位必考证书。 多数企业会选择优先录用持PMP证书的管理人才,PMP成为管理岗位的必考证书。PMP在很多外企和国内中大型企业非常受重视,中石油、中海油、华为等等都会给内部员工做培训。 这些机构对项目管理…...
uniapp 对接腾讯云IM群组成员管理(增删改查)
UniApp 实战:腾讯云IM群组成员管理(增删改查) 一、前言 在社交类App开发中,群组成员管理是核心功能之一。本文将基于UniApp框架,结合腾讯云IM SDK,详细讲解如何实现群组成员的增删改查全流程。 权限校验…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
C++ 求圆面积的程序(Program to find area of a circle)
给定半径r,求圆的面积。圆的面积应精确到小数点后5位。 例子: 输入:r 5 输出:78.53982 解释:由于面积 PI * r * r 3.14159265358979323846 * 5 * 5 78.53982,因为我们只保留小数点后 5 位数字。 输…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...





