当前位置: 首页 > news >正文

线程安全之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的修改.我们要知道对一个变量进行修改一般有三条指令:

  1. 变量从内存中读到某个寄存器上
  2. 对变量进行操作
  3. 将变量从寄存器读回到内存中

那么以上代码就可以用如下图来表示其运行时的某种状态:

因为操作系统的随机调度,导致t1线程可能刚把count=0读取到寄存器上还没开始修改写回时,t2线程就被操作系统安排上了cpu,导致读取到的数据也是count=0,最后当两个线程将其数据分别写回后,内存中count的值就为1.这也就是为什么那段代码的运行结果与预期不符.


1.线程不安全的原因

  • 操作系统的随机调度:这个是造成线程不安全的最根本原因,我们人为也不好去解决.
  • 多线程同时修改同一个变量:多个线程针对同一个线程进行修改,例如以上代码.
  • 修改操作不是原子性:所谓原子操作是指不会被线程调度机制打断的操作.以上代码t1和t2线程对变量的修改操作就不是原子的,例如t1线程还没有将数据修改完写回内存,t2线程就开始读取、修改。
  • 内存不可见:可见性是指一个线程对共享变量值修改后,其他线程能够从内存中去读取而不是从当前CPU的寄存器或高速缓存中读取。反之不可见就是内存中的数据可能已经被某个线程改了,但某些线程仍然在寄存器或高速缓存中读取未修改之前的数据。这样就会导致线程不安全。

内存可见性分析

  1. 线程之间的共享变量存在内存中。然后每个线程都有一个自己的“工作内存”(也就是寄存器 和高速缓存)。
  2. 当某个线程要读取一个共享变量时,会先把变量从内存中拷贝到自己的“工作内存”中,然后从“工作内存”中读取数据;
  3. 当某个线程要修改某个变量时,会直接从自己的“工作内存”去读取之前拷贝的副本,修改后在同步给内存。这样直接在自己的“工作内存”中访问数据虽然可以加快读取速度,但是却存在读错数据的风险,因为无法确定现在线程自己的“工作内存”中的数据是否和主内存中的数据一致.有可能主内存的数据已经被其他线程改了。

所以我们经常说线程安全的可见性是指:一个线程对共享变量值修改后,其他线程能够及时发现,然后从内存中去读取数据,而不是从当前CPU的寄存器或高速缓存中读取。

  • 指令重排序:这里的"序"是指CPU中指令执行的顺序,也就是一条条汇编指令执行的顺序。指令重排序是指CPU会在执行指令前进行优化,即对指令顺序做了调整.指令的重排序也会导致线程不安全。

编译器对指令重排序的前提是”保持逻辑不发生变化,这一点在单线程下很容易判断。但是在多线程下就很难了。多线程的代码执行复杂程度高,编译器很难在编译阶段对代码的执行效果进行预测,因此很容易导致优化后的逻辑和之前不等价.

有序性涉及到CPU以及编译器的一些底层工作原理,就不做过多解释了。


2.synchronized和volatile

2.1 synchronized

synchornized会起到互斥的效果,某个线程执行到某个对象的synchronized中时,其它线程如果也执行到这个对象的synchronized时,就会阻塞等待。

  • 进入synchronized修饰的代码块,相当于对这段代码进行加锁;
  • 退出synchronized 修饰的代码块,相当于对这段代码进行解锁。

就是当t1线程先调用这个被synchronized修饰的方法时,该线程就会获得这把锁,然后进行读取内存,自增,写回内存这些操作。线程走完了这段代码才会将锁打开。而其他线程在t1这个线程获得锁的期间中,不能访问这段代码,只能阻塞等待,直到t1线程释放锁,才能在操作系统的分配下再去竞争这把锁。也就是说,当一个线程获得锁之后,其他线程再想获得这把锁只能阻塞等待,直到这个线程将锁释放。

这时再运行代码时,结果就如预期了:

此期间synchornized的工作过程如下:

  1. 获得互斥锁
  2. 从内存中读取数据副本
  3. 将数据进行修改
  4. 将修改后的数据写入内存
  5. 释放互斥锁

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次&#xff…...

量子计算对网络安全的影响

量子计算的快速发展,例如 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。用于在分布式系统中存储转发消息&#xf…...

并发编程之死锁问题介绍

一、本文概览 死锁问题在并发编程中是一个非常致命的问题,问题一旦产生,只能通过重启机器、修改代码来修复问题,下面我们通过一小段文章内容介绍下死锁以及如何死锁的预防 二、什么是死锁? 在介绍死锁之前,先来明确下什…...

【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北航敏捷软件工程这个作业的要求在哪里个人作业-阅读和提问我在这个课程的目标是体验敏捷开发过程&#xff0c;掌握一些开发技能&#xff0c;为进一步发展作铺垫这个作业在哪个具体方面帮助我实现目标对本课…...

urho3d的自定义文件格式

Urho3D尽可能使用现有文件格式&#xff0c;仅在绝对必要时才定义自定义文件格式。当前使用的自定义文件格式有&#xff1a; 二进制模型格式&#xff08;.mdl&#xff09; Model geometry and vertex morph data byte[4] Identifier "UMDL" or "UMD2" …...

spark第一章:环境安装

系列文章目录 spark第一章&#xff1a;环境安装 文章目录系列文章目录前言一、文件准备1.文件上传2.文件解压3.修改配置4.启动环境二、历史服务器1.修改配置2.启动历史服务器总结前言 spark在大数据环境的重要程度就不必细说了&#xff0c;直接开始吧。 一、文件准备 1.文件…...

MySQL---存储过程与存储函数的相关概念

MySQL—存储过程与存储函数的相关概念 存储函数和存储过程的主要区别&#xff1a; 存储函数一定会有返回值的存储过程不一定有返回值 存储过程和函数能后将复杂的SQL逻辑封装在一起&#xff0c;应用程序无需关注存储过程和函数内部复杂的SQL逻辑&#xff0c;而只需要简单地调…...

PMP值得考吗?

第一&#xff0c;PMP的价值体现 1、PMP是管理岗位必考证书。 多数企业会选择优先录用持PMP证书的管理人才&#xff0c;PMP成为管理岗位的必考证书。PMP在很多外企和国内中大型企业非常受重视&#xff0c;中石油、中海油、华为等等都会给内部员工做培训。 这些机构对项目管理…...

基于算法竞赛的c++编程(28)结构体的进阶应用

结构体的嵌套与复杂数据组织 在C中&#xff0c;结构体可以嵌套使用&#xff0c;形成更复杂的数据结构。例如&#xff0c;可以通过嵌套结构体描述多层级数据关系&#xff1a; struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...

Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件

今天呢&#xff0c;博主的学习进度也是步入了Java Mybatis 框架&#xff0c;目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学&#xff0c;希望能对大家有所帮助&#xff0c;也特别欢迎大家指点不足之处&#xff0c;小生很乐意接受正确的建议&…...

对WWDC 2025 Keynote 内容的预测

借助我们以往对苹果公司发展路径的深入研究经验&#xff0c;以及大语言模型的分析能力&#xff0c;我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际&#xff0c;我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测&#xff0c;聊作存档。等到明…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词

Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵&#xff0c;其中每行&#xff0c;每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid&#xff0c;其中有多少个 3 3 的 “幻方” 子矩阵&am…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

JavaScript基础-API 和 Web API

在学习JavaScript的过程中&#xff0c;理解API&#xff08;应用程序接口&#xff09;和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能&#xff0c;使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配

目录 一、C 内存的基本概念​ 1.1 内存的物理与逻辑结构​ 1.2 C 程序的内存区域划分​ 二、栈内存分配​ 2.1 栈内存的特点​ 2.2 栈内存分配示例​ 三、堆内存分配​ 3.1 new和delete操作符​ 4.2 内存泄漏与悬空指针问题​ 4.3 new和delete的重载​ 四、智能指针…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...