多线程环境下的原子性问题

news2024/10/1 3:23:22

什么是原子性呢?
        在数据库事务的ACID特性中就有原子性,它是指当前操作中包含的多个数据库事务操作,要么全部成功,要么全部失败,不允许存在部分成功、部分失败的情况。而在多线程中的原子性与数据库事务的原子性相同,它是指一个或多个指令操作在CPU执行过程中不允许被中断。

        下面我们来演示一个多线程中出现原子性问题的例子。

public class AtomicExample {
        volatile int i= 0;

        public void incr(){
                i++;

        }
        public static void main(String[] args) throws InterruptedException {
                AtomicExample atomicExample = new AtomicExample();

                Thread[] threads=new Thread[2];

                for (int j= θ;j<2;j++){
                        threads[j]=new Thread(() ->{ for (int k=0;k<10000;k++){
                        atomicExample.incr();

                }

        });
        threads[j].start();

        }
        threads[0].join();//保证线程执行结束 threads[1].join( );
        system.out.println(atomicExample.i);

}

在上述代码中启动了两个线程,每个线程对成员变量i累加10000次,然后打印出累加后的结果。我们从结果中发现,原本期望的值是20000,但是打印出来的i值都是一个小于20000的数。和预期的结果不一致,导致这个现象产生的原因就是原子性问题,

深入分析原子性问题的本质

从本质上说,原子性问题产生的原因有两个。

  • CPU时间片切换。
  • 执行指令的原子性,也就是线程运行的程序或者指令是否具备原子性。

CPU时间片切换


以后会在博客中讲述CPU时间片切换的原理,也就是当CPU不管因为何种原因处于空闲状态时,CPU会把自己的时间片分配给其他线程来处理,整体过程如图所示,CPU通过上下文切换来提升资源利用率。

i++指令的原子性
在Java 程序中,i++操作看起来是一个完整的不可分割的指令,但是实际上并不是这样的。我们通过javap -v命令来查看AtomicExample 类中incr()方法的字节码,运行结果如下。

0:aload_0
1: dup
2: getfield    #2    // Field i:I    
5: iconst_1
6:iadd
7: putfield    #2    // Field i:I    
10: return

可以发现,i++操作实际上是三个指令:getfield、iadd、putficld。

  • getfield,把变量i从内存加载到CPU的寄存器中。
  • indd,在寄存器中执行+1操作。
  • putfield,把结果保存到内存。

需要注意,这三个指令并不具备原子性,也就是说,CPU在执行的过程中会存在中断的情况,这种中断就会导致原子性问题。


如图所示,假设有两个线程同时对变量i进行修改,那么可能的执行过程如下:

  • 线程1先获得CPU的执行权,在CPU将i=0加载到寄存器中后出现线程切换,CPU把执行权切换给线程2并保留当前的CPU上下文。
  • 线程2同样去内存中将i加载到寄存器中进行计算,然后把计算结果写回内存。
  • 线程2释放了CPU资源,线程1重新获得执行权后恢复CPU上下文,而这时i的值还是0。
  • 最终计算后i的结果比预期结果要小。


除上述这种情况外,在多核CPU中,线程的并行执行也会导致原子性问题。如图所示。两个线程并行执行,同时从内存中将i加载到寄存器中并进行计算,最终导致i的结果小于我们的预期值

关于原子性问题的解决办法

通过上述问题的分析,我们发现,多线程环境下线程的并行或切换导致最终执行结果不符合预期,解决问题的办法可以从两个方面考虑。

  • 不允许当前非原子指令在执行过程中被中断,也就是说保证i++操作在执行过程中不存在上下文切换。
  • 多线程并行执行导致的原子性问题可以通过一个互斥条件来实现串行执行。

在Java中,synchronized关键字提供了这样一个功能,在incr()方法上增加synchronized关键字后,可以保证下面这段代码中i变量最终的输出结果必然是20000。

public class AtomicExample { 
    volatile int i= 0;
    public synchronized void incr(){ 
        i++;
    }
    public static void main(String[] args) throws InterruptedException { 
        AtomicExample atomicExample = new AtomicExample(); 
        Thread[] threads=new Thread[2]; 
        for (int j= θ;j<2;j++){
            threads[j]=new Thread(() ->{ 
                for (int k=0;k<10000;k++){ 
                    atomicExample.incr( );
                }
            
            });
            thread[i].start();
        }
        threads[0].join();
        threads[i].join();
        system.out.println(atomicExample.i);
    }
}

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

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

相关文章

WPS中图的自动编号及引用

WPS中图的自动编号及引用 图的自动编号图编号的引用图编号及引用的更新 图的自动编号 将光标放置在需要插入编号的位置点击“引用”→“题注”&#xff1a; 点击“引用”→“题注”&#xff1a; 点击“编号”&#xff0c;设置图的编号格式&#xff0c;可勾选“包含章节编号”&…

【RTOS学习】信号量 | 互斥量 | 递归锁

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《RTOS学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 信号量 | 互斥量 | 递归锁 &#x1f37a;信号量&#x1f964;原理&#x1f964;使用信号量的函数&…

Java反射获取内部类方法

Java反射获取内部类方法 结论一、案例准备二、测试方法&#xff1a;使用反射获取类的成员内部类和方法具体操作具体操作&#xff08;使用getDeclaredClasses&#xff09; 结论 Java 通过反射可以获得内部类&#xff0c;包括内部类属性信息和方法。 一、案例准备 创建了一个类…

1024,向着“顶尖程序员“迈进

10月24日&#xff0c;对每个程序员而言&#xff0c;都是一个具有特殊意义的日子。1024这个数字&#xff0c;不再只是计算机存储容量的基础单位&#xff0c;更是我们向着技术巅峰进发的象征。 回顾我的程序员之路&#xff0c;那是一个不断学习、不断成长的过程。起初是对编程充…

『第二章』这只燕子很特别:Swift 特性

在本篇博文中,您将学到如下内容: 1. Swift 语言概览2. Objective-C “练废了”&#xff0c;重新写一门新语言吧&#xff01;3. Swift 的“习性”与优势3.1. Swift 更简洁、更易于阅读、所需代码更少3.2. Swift 更加安全3.3. Swift 内存管理更加统一3.4. Swift 更快3.5. Swift 会…

Redis主从模式(二)---拓扑结构及复制过程

目录 一, Redis主从模式下的复制拓扑结构 1.1 一主一从结构 1.2 一主多从结构 1.3 树形主从结构 二, 主从复制过程 2.1 主从复制建立复制流程图 2.2 数据同步(psyc) 1.replicationid/replid (复制id) 2.offset(偏移量) 2.3 psync运行流程 2.4 全量复制 2.5 部分复制…

Opencv-图像插值与LUT查找表

图像像素的比较 白色是255&#xff0c;黑色是0 min(InputArray src1,InputArray src2,OutputArray dst) max(InputArray src1,InputArray src2,OutpurArray dstsrc1:第一个图像矩阵&#xff0c;通道数任意src2&#xff1a;第二个图像矩阵&#xff0c;尺寸和通道数以及数据类型…

【C++面向对象】5. this指针

文章目录 【 1. 基本原理 】【 2. 实例 】 【 1. 基本原理 】 在 C 中&#xff0c;只有成员函数才有 this 指针&#xff08;友元函数没有 this 指针&#xff0c;因为友元不是类的成员&#xff09;&#xff0c;this 指针是所有成员函数的隐含参数。 在成员函数内部&#xff0c;…

用*画田字形状,numpy和字符串格式化都可以胜任

numpy的字符型元素矩阵&#xff0c;可以方便画&#xff1b;直接python字符串手撕&#xff0c;也可以轻巧完成。 (本笔记适合熟悉循环和列表的 coder 翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大咖免费“圣经”教程《…

【WinForm详细教程一】WinForm中的窗体、Label、TextBox及Button控件、RadioButton和CheckBox、ListBox

文章目录 1.WinForm文件结构2. 窗体的常用属性、方法与事件2.1 常用属性&#xff08;可直接在属性中设置&#xff09;2.2 常用方法2.3 常用事件 3.Label、TextBox及Button控件4.RadioButton和CheckBox5.ListBox&#xff08;列表框&#xff09; 1.WinForm文件结构 .sln文件 &am…

IEEE754 标准存储浮点数

1. IEEE754 标准简介 IEEE754 标准是一种用于浮点数表示和运算的标准&#xff0c;由国际电工委员会&#xff08;IEEE&#xff09;制定。它定义了浮点数的编码格式、舍入规则以及基本的算术运算规则&#xff0c;旨在提供一种可移植性和一致性的方式来表示和处理浮点数 IEEE754 …

浅谈电力电容器的故障处理及选型

安科瑞 华楠 【摘要】常见的电力电容器都是为了改善电力系统的电压质量和提高输电线路的输电能力&#xff0c;它们在减少系统功率损耗、提高功率因数、降低运行电流、提升电网电压、释放变压器使用裕度等方面有着显著效果。按电压等级可以划分高、低压两部分。虽然它们可以起着…

Vue mixin混入

可以把多个组件中共有的配置提取出来构成一个混入。 一、配置混入 &#xff08;一&#xff09; 创建mixin.js 这里的名字可以自定义&#xff0c;但是为了方便识别&#xff0c;多数场景下都写mixin。 mixin.js 要创建在src目录下&#xff0c;与main.js平级&#xff1a; &…

win10启动venv报错:无法加载文件 venv\Scripts\activate.ps1,因为在此系统上禁止运行脚本。

背景&#xff1a; 最近需要用到python开发&#xff0c;切换虚拟环境时&#xff0c;在win10系统上安装编辑器后创建了虚拟环境&#xff0c;但是执行activate时报错&#xff1a;.\venv\Scripts\activate&#xff0c;报错内容如题&#xff1a; 无法加载文件 venv\Scripts\activa…

HTTPSConnectionPool(host=‘huggingface.co‘, port=443)解决

huggingface&#xff0c;也就是抱抱脸&#xff0c;应该都很熟悉了吧 好用是很好用&#xff0c;就是有一个问题&#xff0c;国内的IP地址总是不灵是吧 今天我就碰到这么个问题 请看图&#xff1a; 。。。。图找不到了&#xff0c;我的问题忘记记录了 我给你们贴上文字吧 (M…

reactNative导入excel文件

组件内导入 import {TouchableOpacity,PermissionsAndroid} from react-native; import RNFS from react-native-fs; import XLSX from xlsx; import DocumentPicker from react-native-document-picker; import {Buffer} from buffer;// 需要安装一下三个,Buffer和react-nati…

Standford Compiler Course Assignment 2

第二部分的作业是语法分析&#xff0c;通过编写cool.y(这个assignment的任务)&#xff0c;利用bison将其自动生成语法分析LALR(1)的代码。 语法分析&#xff0c;就是将词法分析阶段已经识别好的token&#xff0c;按照语法的规则&#xff0c;构建抽象语法树的过程。 比如以下的…

读书笔记之《敏捷测试从零开始》(一)

大家好&#xff0c;我是rainbowzhou。 子曰&#xff1a;学而时习之&#xff0c;不亦说乎&#xff1f;今天我想和大家分享一本测试书籍——《敏捷测试从零开始》。以下为我的读书笔记&#xff1a; 精彩片段摘录&#xff1a; 焦虑往往来自于对比&#xff0c;当你在自己的圈子里面…

Elasticsearch之mapping

文章目录 以显式的方式创建一个映射查看某个具体索引的mapping定义向已存在的映射中添加一个新的属性查看映射中指定字段的定义信息更新已存在映射的某个字段 1、 官方文档地址 2、 字段类型 1、定义&#xff1a;映射是定义文档及其包含的字段如何存储和索引的过程。 2、每个…

LabVIEW在 XY Graph中选择一组点

LabVIEW在 XY Graph中选择一组点 问题&#xff1a;有一个包含许多点的XY Graph&#xff0c;在程序开发中&#xff0c;对于显示XY Graph中的多个点&#xff0c;如何进行选取。最好能像图像处理中的ROI一样&#xff0c;并且它们的颜色可以更改&#xff0c;可以在其中选择一些ROI…