热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JUC之比较交换CAS

——CAS(CompareAndSwa

—— CAS(Compare And Swap)


没有 CAS 之前,多线程环境下不使用原子类保证线程安全 i++,只能通过 synchronized 加锁的方式,高并发多写情况下,性能影响很大;使用 CAS 之后,可以使用原子类(Atomic )保证线程安全,类似于 乐观锁


基本概念 & 底层原理

原理

  • CAS(compare and swap):比较并交换,是一条 CPU 并发原语,它包含 3 个操作数——内存位置、预期原值、更新值(主内存值、工作内存值、更新值)
    • 执行 CAS 操作的时候,将内存位置的值与预期原值比较;
    • 如果 匹配, 那么处理器会自动将 位置值 更新为 新值
    • 如果 不匹配,处理器不做任何操作,多个贤臣同时执行 CAS 操作 只有一个会成功
    • 当且仅当旧的预期值 和 内存值 相同时,将 内存值 修改为 更新值,否则什么都不做或重来,当它重来重试的这种行为称为 自旋
      在这里插入图片描述

硬件保证

  • CAS 是 JDK 提供的非阻塞原子性操作,它通过 硬件 保证了比较-更新的原子性,它是非阻塞的且自身具有原子性,效率更高,且通过硬件保证,更可靠
  • CAS 是一条 CPU 的原子指令(cmpxchg指令【compare x change】),不会造成所谓的数据不一致问题,Unsafe 提供的 CAS 方法底层实现即为 CPU 指令 cmpxchg
    • 执行cmpxchg指令时,会判断当前系统是否为多核系统,如果是就给总线加锁,只有一个线程会对总线加锁成功,加锁成功之后会执行 CAS 操作,也就是说 CAS的原子性实际上是 CPU 实现独占的,比起用 synchronized 重量级锁,这里的排他时间要短很多,所以在多线程情况下性能会更好

源码说明

public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}

  • 参数说明
    • this:表示要操作的对象
    • valueOffset:表示要操作对象中属性地址的偏移量
    • expect:表示需要修改数据的期望的值
    • update:表示需要修改为的新值

Unsafe 类详解

  • 是 CAS 的核心类,由于 Java 方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe 相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe 类存在于 sun.misc 包中,其内部方法操作可以像 C 的指针一样直接操作内存,因为 Java 中 CAS 操作的执行依赖于 Unsafe 类的方法
  • Unsafe 类中的所有方法都是 native 修饰的,所以 Unsafe 类中的方法都直接调用操作系统底层资源执行相应任务
  • 变量 valueOffset,Unsafe 就是根据内存偏移地址来获取数据的
  • 变量 value 用 volatile 修饰,保证了多线程之间的内存可见性
  • AtomicInteger 类主要利用 CAS + volatile + native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升
  • CAS 并发原语体现在 Java 语言中就是 sun.misc.Unsafe 类中的各个方法。调用 Unsafe 类中的 CAS 方法,JVM 会帮我们实现出 CAS 汇编指令。这是一种完全依赖于硬件的功能,通过它实现了原子操作。由于 CAS 是一种系统原语,原语属于操作系统用语范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被打断,也就是说 CAS 是一条 CPU 的原子指令,不会造成所谓的数据不一致问题

总结

  • CAS 是靠硬件实现的,从而在硬件层面提升效率,最底层还是交给硬件来保证原子性和可见性
  • 实现方式是基于硬件平台的汇编指令,在 intel 的 CPU 中(X86机器),使用的是汇编指令 cmpxchg 指令
  • 核心思想是:比较要更新变量的值V和预期值E,相等才会将V的值设置为新值 N,如果不相等自旋

缺点

  • 自旋引起的循环时间长,CPU 开销大
  • ABA 问题
    • CAS 算法实现一个重要前提是,需要取出内存中某个时刻的数据并在当下时刻比较并交换,那么在这个时间差会导致数据的变化
    • 比如一个线程A 从内存位置取出 1,另一个线程B 也从内存中取出 1,并且线程 B 进行了一些操作将 1 改为 2,然后线程B 又将 2 改回了 1,这时线程 A 进行CAS 操作发现内存中仍然是 1,预期 OK ,进行操作更改
    • 尽管线程 A 的 CAS 操作成功,但是不代表这个过程就是没有问题的

通过版本号戳记流水原子引用(AtomicStampedReference)可解决 ABA 问题

—— 原子类


基本类型原子类


  • AtomicInteger
  • AtomicBolean
  • AtomicLong

数组类型原子类


  • AtomicIntegerArray
  • AtomicLongArray
  • AtomicReferenceArray

引用类型原子类

AtomicReference

AtomicStampedReference

  • 携带版本号的引用类型原子类,可以解决 ABA 问题
  • 解决修改过几次的问题
  • 版本号流水戳原子引用

AtomicMarkableReference

  • 原子更新带有标记位的引用类型对象
  • 解决是否修改过(就是将版本号简化为 true|false,类似于一次性使用)
  • 状态戳(true、false)原子引用

对象的属性修改原子类


  • AtomicIntegerFieldUpdater
  • AtomicLongFieldUpdater
  • AtomicReferenceFieldUpdater

原子操作增强类


  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder

推荐阅读
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • JVM:33 如何查看JVM的Full GC日志
    1.示例代码packagecom.webcode;publicclassDemo4{publicstaticvoidmain(String[]args){byte[]arr ... [详细]
  • 生产环境下JVM调优参数的设置实例
     正文前先来一波福利推荐: 福利一:百万年薪架构师视频,该视频可以学到很多东西,是本人花钱买的VIP课程,学习消化了一年,为了支持一下女朋友公众号也方便大家学习,共享给大家。福利二 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 服务器上的操作系统有哪些,如何选择适合的操作系统?
    本文介绍了服务器上常见的操作系统,包括系统盘镜像、数据盘镜像和整机镜像的数量。同时,还介绍了共享镜像的限制和使用方法。此外,还提供了关于华为云服务的帮助中心,其中包括产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题和视频帮助等技术文档。对于裸金属服务器的远程登录,本文介绍了使用密钥对登录的方法,并提供了部分操作系统配置示例。最后,还提到了SUSE云耀云服务器的特点和快速搭建方法。 ... [详细]
  • 关于CMS收集器的知识介绍和优缺点分析
    本文介绍了CMS收集器的概念、运行过程和优缺点,并解释了垃圾回收器的作用和实践。CMS收集器是一种基于标记-清除算法的垃圾回收器,适用于互联网站和B/S系统等对响应速度和停顿时间有较高要求的应用。同时,还提供了其他垃圾回收器的参考资料。 ... [详细]
  • RouterOS 5.16软路由安装图解教程
    本文介绍了如何安装RouterOS 5.16软路由系统,包括系统要求、安装步骤和登录方式。同时提供了详细的图解教程,方便读者进行操作。 ... [详细]
  • 嵌入式处理器的架构与内核发展历程
    本文主要介绍了嵌入式处理器的架构与内核发展历程,包括不同架构的指令集的变化,以及内核的流水线和结构。通过对ARM架构的分析,可以更好地理解嵌入式处理器的架构与内核的关系。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 初识java关于JDK、JRE、JVM 了解一下 ... [详细]
author-avatar
手机用户2502862133
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有