多线程(八):并发编程的三大特性之原子性

2023-11-07

目录

通过一个demo认识原子性

java中只有以下操作是原子性的

上锁的本质

那么如何保障数据的一致性呢?

如何理解锁的粒度?

如何保障操作的原子性?

什么是悲观锁?

什么是乐观锁?

什么是CAS?

什么是ABA问题?

乐观锁和悲观锁哪种效率跟高?

synchronized的三大特性


通过一个demo认识原子性

大家先来看一个小demo

public class Demo01 {

    private static long n = 0L;

    public static void main(String[] args) throws InterruptedException {

        Thread[] threads = new Thread[100];

        CountDownLatch cd = new CountDownLatch(threads.length);

        for (int i=0;i<threads.length;i++){
            threads[i] = new Thread(()->{

                for(int j=0;j<1000;j++){
                    n++;
                }
                cd.countDown();
            });
        }

        for(Thread t: threads){
            t.start();
        }

        cd.await();

        System.out.println("n = " + n);

    }
}

问:这个Demo执行完成之后打印n的值为1000000吗?

答案是:不是的

 在这个demo中,我们用了100个线程去执行n++的操作,每个线程执行10000次n++的操作,理论上执行完成之后的结果应该是1000000才对,为什么不是呢?

看图:

图解:每个线程都需要从内存中读出n到寄存器中,然后执行n++的操作,执行完成之后在写入到内存中。我们都知道,多线程异步执行的。比如说thread01读到n之后,做完n++操作之后还没来得及给内存中更新,thread02读到了n,并且对n进行了n++的操作更新到了内存中,这时候thread01在去内存中更新n的值,是不是就重叠了。所以n++不是一个原子性的操作,原子性就是要么成功,要么失败,没有中间状态。

不了解寄存器概念的童鞋可以到我主页看我多线程的第一篇文章,里面通俗简答的介绍了寄存器,看懂必理解!!!

java中只有以下操作是原子性的

  1. lock:主内存,标识变量为线程独占
  2. unlock:主内存,解锁线程独占变量
  3. read:主内存,读取内存到线程缓存(工作内存)
  4. load:工作内存,read后的值放入线程本地变量副本
  5. use:工作内存,传值给执行引擎
  6. assign:工作内存,执行引擎结果赋值给线程本地变量
  7. store:工作内存,存值到主内存给write备用
  8. write:主内存,写变量值

注意:这些操作都是虚拟机级别的操作,不是语句级别的操作。

只要我们不确定这条语句是不是原子性的,上锁就可以,就这么简单!!!

所以上边那个小demo,只要我们对n++操作进行上锁,那么最终打印出来的结果肯定是1000000。

上锁的本质

小边那个demo告诉我们了多线程访问统一条数据的时候会产生竞争条件,产生竞争条件就有可能产生数据不一致的问题。

那么如何保障数据的一致性呢?

保障数据的一致性就要保证线程同步(线程执行的顺序事先安排好)。如何把线程执行的顺序安排好呢?我们就要通过上锁的方式来实现线程同步。让线程的执行顺序变的序列化起来。

如何理解锁的粒度?

上完锁之后锁定的代码我们成为临界区,临界区越大(也就是锁住的代码越多,执行代码的时间越长)锁的粒度越粗,反之,临界区越小,锁的粒度越小。

如何保障操作的原子性?

 通过上锁保障操作的原子性,两种方式,一种是悲观锁,一种是乐观锁。

什么是悲观锁?

悲观的认为这个线程执行的操作会被别的线程打断(悲观锁),典型的悲观锁就是synchronized。

什么是乐观锁?

乐观的认为这个线程执行的操作不会被别的线程打断(乐观锁),乐观锁又称之为无锁、自旋锁、CAS(compare and swap)

什么是CAS?

就拿上面demo举例子,看图:

认真看完图并看完图中的文字,我想大家应该已经理解什么是CAS,当然,说到CAS,很多了解他的小伙伴就会想到其中的ABA问题了。 下面会说。

什么是ABA问题?

还拿上面的demo举例子,比如说现在有三个线程,要修改n的值

第一个线程(thread01),第二个线程(thread02),第三个线程(thread03)

现在n的值为0,thread01把a读出来为0,进行n++的操作,往回写的时候判断内存n的值依旧是0,所以看似没什么问题,直接更新过去了。但是当thread01判断内存中n的值依旧为0之前,thread02把n修改为了8,thread02修改完之后,thread03又把n修改为了0,所以thread01看到内存中n的值其实是thread03修改完成后的n的值,此0非彼0。这就是ABA问题,本来n的值为0(A),thread02把n的值修改为了8(B),thread02修改完之后,thread03又把n修改为了0(A)。

当然,这个小demo之后ABA问题我们可以忽略,因为最后它还是修改为了1,数据是一致的,但是如果n不是int类型,而是一个对象的引用类型,发生了ABA问题的话,虽然引用没有变,但是对象里面的内容已经改变了,这时候ABA问题就需要解决了。解决也非常简单,只需要在对象中增加一个字段version(版本号),每次对他修改完成之后把版本号+1,然后判断这个对象有没有改变的时候判断他的版本号有没有改变,就可以完美的解决ABA问题了。

乐观锁和悲观锁哪种效率跟高?

首先我们需要了解悲观锁实现方式,悲观锁大致实现是:

有一个队列,比如说有三个线程:thread01,thread02,thread03。thread01先来了,发现这段代码没有人正在在执行,好,这时候thread01执行,然后锁住。然后thread02和thread03来了,发现代码在上锁的状态,就会放到队列中,处于阻塞状态。注意:阻塞状态是不消耗cpu资源的。然后等thread01执行完成并且释放锁了,thread02再去,然后thread03......与此形成鲜明的对比的正是乐观锁,乐观锁会一致自旋的去判断上一个线程是否已经释放锁了,注意:自旋是消耗cpu资源的。等待队列是不一样的,他是不占用cpu的。

所以,假如锁的粒度大,执行的时间长,等待执行的线程多,用悲观锁的。反之,用乐观锁。

synchronized的三大特性

synchronized保证了原子性、一致性、可见性。

首先synchronized锁住的代码是原子性的,其次比如说有两个线程执行同一段代码,如果加了锁之后会保证他们序列化的执行,所以synchronized也保证了一致性,那它如何保证可见性的呢?synchronized会上锁,对应的也会解锁(unlock),unlock操作会把内存的状态和本地缓存的状态做一个刷新,所以也保证了可见性。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多线程(八):并发编程的三大特性之原子性 的相关文章

  • Android:如何暂停和恢复可运行线程?

    我正在使用 postDelayed 可运行线程 当我按下按钮时 我需要暂停并恢复该线程 请任何人帮助我 这是我的主题 protected void animation music6 music4 postDelayed new Runnab
  • java程序有多少种结束方式?

    我知道使用 System exit 0 可以结束一个java程序 例如 如果我有一个JFrame窗口 它会关闭并结束程序 但我想知道还有多少其他方法 可以关闭它并结束程序 包括发生错误时 程序会被关闭 JFrame也会被关闭吗 添加到其他答
  • 如何以编程方式使用包含多列的 where-in 子句执行 PostgreSQL 查询?

    我的查询是这样的 select from plat customs complex where code t code s in 01013090 10 01029010 90 它在 psql 控制台中运行良好 我的问题是如何在客户端代码中
  • 我们可以有条件地声明 spring bean 吗?

    有没有一种方法可以有条件地声明 Spring bean 例如
  • 有人用过 ServiceLoader 和 Guice 一起使用吗?

    我一直想通过我们的应用程序 构建系统进行更大规模的尝试 但更高的优先级不断将其推到次要地位 这似乎是加载 Guice 模块的好方法 并且避免了关于 硬编码配置 的常见抱怨 单个配置属性很少会自行更改 但您几乎总是会有一组配置文件 通常用于不
  • 自定义列表字段点击事件

    我正在编写一个应用程序 其中我创建了用于显示列表视图的自定义列表字段 我的 CustomListField 包含连续的一个图像和文本 我正在通过单击列表字段行获取字段更改侦听器 但我也想将字段更改侦听器放在图像上 谁能告诉我我该怎么做 这是
  • 使用 OkHttp 下载损坏的文件

    我编写的下载文件的方法总是会产生损坏的文件 public static String okDownloadToFileSync final String link final String fileName final boolean te
  • Java AES 256 加密

    我有下面的 java 代码来加密使用 64 个字符密钥的字符串 我的问题是这会是 AES 256 加密吗 String keyString C0BAE23DF8B51807B3E17D21925FADF273A70181E1D81B8EDE
  • 如何更改 Swagger-ui URL 前缀?

    我正在使用 Springfox Swagger2 和 Spring boot 1 5 9 我可以通过此链接访问 swagger UI http localhost 8090 swagger ui html http localhost 80
  • 为什么解析这个 JSON 会抛出错误?

    我正在尝试解析这个 JSONObject query yahoo count 1 results rate Name USD INR id USDINR Time 12 19pm Date 10 31 2015 Bid 65 405 Ask
  • 如何在 IntelliJ IDEA 中运行 akka actor

    来自 Akka 网站文档 然后 这个主要方法将创建所需的基础设施 运行演员 启动给定的主要演员并安排 一旦主要参与者终止 整个应用程序就会关闭 因此 您将能够使用类似于以下的命令运行上面的代码 下列的 java classpath akka
  • Android - 存储对ApplicationContext的引用

    我有一个静态 Preferences 类 其中包含一些应用程序首选项和类似的内容 可以在那里存储对 ApplicationContext 的引用吗 我需要该引用 以便我可以在不继承 Activity 的类中获取缓存文件夹和类似内容 你使用的
  • Joshua Bloch 的构建器设计模式有何改进?

    早在 2007 年 我就读过一篇关于 Joshua Blochs 所采用的 构建器模式 的文章 以及如何修改它以改善构造函数和 setter 的过度使用 特别是当对象具有大量属性 其中大部分属性是可选的 时 本文对此设计模式进行了简要总结
  • 如何向页面添加 HTML 页眉和页脚?

    如何使用 itext 从 html 源添加标题到 pdf 目前 我们已经扩展了 PdfPageEventHelper 并重写了这些方法 工作正常 但当我到达 2 个以上页面时 它会抛出 RuntimeWorkerException Over
  • 如何使用 Jersey 将嵌套列表封送为 JSON?我得到一个空数组或一个包含数组的单元素字典数组

    我正在开发一个使用 Jersey 将对象转换为 JSON 的项目 我希望能够写出嵌套列表 如下所示 data one two three a b c 我想要转换的对象首先将数据表示为 gt gt 我认为 Jersey 会做正确的事情 以上输
  • 我所有的 java 应用程序现在都会抛出 java.awt.headlessException

    所以几天前我有几个工作Java应用程序使用Swing图书馆 JFrame尤其 他们都工作得很好 现在他们都抛出了这个异常 java awt headlessexception 我不知道是什么改变了也许我的Java版本不小心更新了 谢谢你尽你
  • 用于请求带有临时缓存的远程 Observable 的 RxJava 模式

    用例是这样的 我想暂时缓存最新发出的昂贵的Observable响应 但在它过期后 返回到昂贵的源Observable并再次缓存它 等等 一个非常基本的网络缓存场景 但我真的很难让它工作 private Observable
  • Java 的“&&”与“&”运算符

    我使用的示例来自 Java Herbert Schildt 的完整参考文献 第 12 版 Java 是 14 他给出了以下 2 个示例 如果阻止 第一个是好的 第二个是错误的 因此发表评论 public class PatternMatch
  • Spring 作为 JNDI 提供者?

    我想使用 Spring 作为 JNDI 提供程序 这意味着我想在 Spring 上下文中配置一个 bean 可以通过 JNDI 访问该 bean 这看起来像这样
  • 关闭扫描仪是否会影响性能

    我正在解决一个竞争问题 在问题中 我正在使用扫描仪获取用户输入 这是 2 个代码段 一个关闭扫描器 一个不关闭扫描器 关闭扫描仪 import java util Scanner public class JImSelection publ

随机推荐

  • shell脚本输入密码

    平时在控制台输入指令如 sudo ssh ftp或者修改admin权限的文件时候都会要求输入password 但是在she ll脚本运行过程中该如何交互实现自动输入密码呢 下面总结三种实现方法 一 重定向 用重定向方法实现交互的前提是指令需
  • Linux 基础语法 -2

    如果我们以后再Linux当中 写了一些命名 导致程序我们不能进行操作了 如这个死循环 他就会一直输出 hello Linux 我们就使用 ctrl c 来终止因为程序或者指令异常 而导致我们无法进行指令输入 通配符 它的意思是所有 比如我们
  • python合并音频Couldn‘t find ffprobe or avprobe解决办法

    1 cmd指令pip install pydub 安装pydub库 当然 还需要ffmpeg库 pip install ffmpeg安装或者下载ffmpeg https ffmpeg zeranoe com builds 解压安装后将路径复
  • 解析C++中不能重载为友元函数的四个运算符

    C 规定有四个运算符 gt 不可以是全局域中的重载 即不能重载为友员函数 这是为什么呢 现在先说说赋值运算符 的重载 C 规定赋值运算符 只能重载为类的非静态成员函数 而不可以重载为类的友元函数 不能重载为类的静态成员应该比较容易理解 因为
  • @SpyBean 和 @MockBean 区别,以及@Spy 和 @Mock的区别

    2019独角兽企业重金招聘Python工程师标准 gt gt gt spy对象和mock对象的两点区别 Spy 和 Mock的 两点 区别 SpyBean 和 MockBean 的两点区别 1 默认行为的不同 对于未指定mock的方法 sp
  • centos7 安装/卸载 任意版本的mariadb(mysql)

    mysql官网的链接 https downloads mysql com archives community 安装 mariadb是mysql的一个开源分支 安装mariadb后使用的命令依旧是mysql的 因此这里博主使用centos7
  • 彻底解决SLF4J的日志冲突的问题

    今天公司同事上线时发现 有的机器打印了日志 而有的机器则一条日志也没有打 以往都是没有问题的 因此猜测是这次开发间接引入新的日志jar包 日志冲突导致未打印 排查代码发现 系统使用的是SLF4J框架打印log4j2的日志 查看系统中引入的j
  • openssl库HMAC使用 undefined reference to `HMAC_CTX_new‘

    原因 由于使用的openssl库版本的问题HMAC CTX HMAC CTX new void 为OpenSSL 1 1 0后开始引入的函数 老的库要使用void HMAC CTX init HMAC CTX ctx 老的 HMAC库函数
  • Unix and perl primer for Biologists - Part1 :Unix - Learning the Essentials - Reading Notes(U25-U32)

    U25 Less is more you can look at files in Unix the less command lets you view but not edit text files When you are using
  • Java自学之路-马士兵老师

    JAVA自学之路 一 学会选择 为了就业 不少同学参加各种各样的培训 决心做软件的 大多数人选的是java 或是 net 也有一些选择了手机 嵌入式 游戏 3G 测试等 那么究竟应该选择什么方向呢 我的意见是 不要太过相信各种培训机构或是抢
  • 毕业论文中去掉Latex超链接颜色

    毕业论文中去掉Latex超链接 目录 颜色文献颜色 方法一 隐藏链接 hypersetup hidelinks 方法二 修改链接颜色 hypersetup colorlinks true linkcolor black
  • 剑指offer_第19题_顺时针打印矩阵_Python

    题目描述 输入一个矩阵 按照从外向里以顺时针的顺序依次打印出每一个数字 例如 如果输入如下4 X 4矩阵 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1 2 3 4 8 12 16 15 1
  • react 创建新页面_React 初学者教程2: 创建第一个 React 应用

    通过前面一章的学习 我们已经了解了有关 React 的基本信息 以及它是如何帮助我们轻松创建即使是最复杂的用户界面 但是对于 React 提供的所有精彩来说 刚开始学习它可不是最简单的事情 React 的学习曲线相当陡峭 里面大大小小的障碍
  • 论文理解【Offline RL】——【BooT】Bootstrapped Transformer for Offline Reinforcement Learning

    标题 Bootstrapped Transformer for Offline Reinforcement Learning 文章链接 Bootstrapped Transformer for Offline Reinforcement L
  • switch语句解决ATN取款机问题

    include
  • Parasoft C/C++test更新至v10.4.2版本,静态分析性能增强!

    Parasoft C C test是针对 C C 开发的综合性代码质量保障工具 有效提高开发团队工作效率和软件质量 1月29日 MISRA和AUTOSAR联盟宣布合并两项最受欢迎的安全关键C 开发编码标准 澄清了两个关键问题 将更新AUTO
  • A Survey on VQA: Datasets and Approaches

    VQA综述类论文的阅读 VQA综述类论文的阅读 A Survey on VQA Datasets and Approaches 介绍 数据集 图像类数据集 Balanced VQA Datasets of Statistical Figur
  • Cglib动态代理

    JDK实现动态代理需要实现类通过接口定义业务方法 对于没有接口的类 如何实现动态代理呢 这就需要CGLib了 CGLib采用了非常底层的字节码技术 其原理是通过字节码技术为一个类创建子类 并在子类中采用方法拦截的技术拦截所有父类方法的调用
  • 远程计算机需要网络级别身份验证,而您的计算机不支持该验证的解决方法

    故障 远程计算机需要网络级别身份验证 而您的计算机不支持该验证 请联系您的系统管理员或者技术人员来获得帮助 故障症状 当您使用Windows XP 远程桌面连接 工具去连接Windows Vistas或Windows Server 2008
  • 多线程(八):并发编程的三大特性之原子性

    目录 通过一个demo认识原子性 java中只有以下操作是原子性的 上锁的本质 那么如何保障数据的一致性呢 如何理解锁的粒度 如何保障操作的原子性 什么是悲观锁 什么是乐观锁 什么是CAS 什么是ABA问题 乐观锁和悲观锁哪种效率跟高 sy