【JAVA】CAS总结

news/2024/11/7 18:48:16/

什么是CAS

CAS的全称为Compare-And-Swap,直译就是对比交换。是一条CPU的原子指令,其作用是让CPU先进行比较两个值是否相等,然后原子地更新某个位置的值,其实现方式是基于硬件平台的汇编指令,就是说CAS是靠硬件实现的,JVM只是封装了汇编调用。

简单解释:CAS操作需要输入两个数值,一个旧值(期望操作前的值)和一个新值,在操作期间先比较下在旧值有没有发生变化,如果没有发生变化,才交换成新值,发生了变化则不交换。

CAS操作是原子性的,所以多线程并发使用CAS更新数据时,可以不使用锁。

CAS的优缺点

优点:

  1. 由于CAS是非阻塞的,可避免死锁,线程间的互相影响非常小。
  2. 没有锁竞争带来的系统开销,也没有线程间频繁调度的开销。

缺点:

  1. 可能自旋循环时间过长。如果某个线程通过CAS方式操作某个变量不成功,长时间自旋,则会对CPU带来较大开销。怎么解决:限制自旋次数。
  2. 只是一个变量的原子性操作,不能保证代码块的原子性。
  3. ABA问题。

CAS底层原理

  1. java 的 cas 利用的的是 unsafe 这个类提供的 cas 操作。
  2. unsafe 的cas 依赖了的是 jvm 针对不同的操作系统实现的 Atomic::cmpxchg
  3. Atomic::cmpxchg 的实现使用了汇编的 cas 操作,并使用 cpu 硬件提供的 lock信号保证其原子性

CAS 可能会导致什么问题?

CAS存在ABA问题:

  • 当前线程只能感知共享变量的值有没有改变,假设别人从A改成了B,又改成了A,则当前线程感知不到,即ABA问题

  • 可以使用版本号机制解决ABA问题,如原子类AtomicStampedReference,加入了版本号,每当修改值,版本号就会+1

  • 如 AtomicStampedReference 加入了版本号,每当修改值,版本号就会+1。这样根据版本号就知道有没有别的线程修改过,而且还能知道修改过几次

  • 再如 AtomicMarkableReference,他的版本号是一个boolean类型,所以只能判断是否修改过值,不知道修改过几次。

    AtomicMarkableReference只能缓解ABA问题,如果别人修改标记后,又改回来,则无法感知。

CAS 机制可以高效地实现原子操作,但仍不完美:

  1. 循环时间长开销大:CAS 大量失败后长时间占用 CPU 资源,加大了系统性能开销
  2. 只能保证一个共享变量的原子操作:当对一个共享变量执行操作时,我们可以使用循环 CAS 的方式来保证原子操作,但是对多个共享变量操作时,循环 CAS 就无法保证操作的原子性
  3. ABA 问题:CAS 机制本质上依赖值有没有发生变化的操作条件。但是如果值原来是 A、被改成变成了 B、最后又变回了 A,那么使用 CAS 进行检查时会发现它的值没有发生变化进行了操作,但是实际上却变化了,这其实违背了约定的条件。

Java的JUC包里有没有现成的类可以解决CAS的ABA问题?

  • 原子引用:(乐观锁思想) AtomicStampedReference 类可以解决ABA问题。这个类维护了一个【版本号】Stamp,其实有点类似乐观锁的意思。在进行 CAS 操作的时候,不仅要比较当前值,还要比较版本号。只有两者都相等,才执行更新操作。
/*** @description: 解决 CAS 带来的 ABA问题*/
public class ABADemo2 {//6为传入的值,1为版本号static AtomicStampedReference<Integer> atomic = new AtomicStampedReference<>(6, 1);public static void main(String[] args) {//使用版本号机制来验证ABA问题new Thread(() -> {//获取当前版本号int stamp = atomic.getStamp();System.out.println("线程A1的版本号为:" + stamp);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//如果当前引用 等于 预期值并且 当前版本戳等于预期版本戳, 将更新新的引用和新的版本戳到内存//compareAndSet(V expectedReference,V newReference,int expectedStamp,int newStamp);atomic.compareAndSet(6, 10, atomic.getStamp(), atomic.getStamp() + 1);System.out.println("线程A2的版本号为:" + atomic.getStamp());System.out.println(atomic.compareAndSet(10, 6, atomic.getStamp(), atomic.getStamp() + 1));System.out.println("线程A3的版本号为:" + atomic.getStamp());}, "线程A:").start();//使用【乐观锁】思想解决ABA问题new Thread(() -> {int stamp = atomic.getStamp();System.out.println("线程B1的版本号为:" + stamp);try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(atomic.compareAndSet(6, 2, stamp, stamp + 1));System.out.println("线程B2的版本号为:" + atomic.getStamp());}, "线程B:").start();}
}
  • AtomicStampedReference<>(6, 1); 这两个参数不要填的太大,因为我们本次测试用的是 Integer 类型
  • Integer 使用了对象缓存机制,默认范围是 -128 ~ 127 ,推荐使用静态工厂方法 valueOf 获取对象实例,而不是 new,因为 valueOf 使用缓存,而 new一定会创建新的对象分配新的内存空间。

为什么会导致ABA问题?

这是因为 CAS 算法是在某一时刻取出内存值然后在当前的时刻进行比较,中间存在一个时间差,在这个时间差里就可能会产生 ABA 问题。

Java哪些地方使用了CAS?

Java提供的API中使用CAS的地方有很多,比较典型的使用场景有原子类、AQS、并发容器

  • 对于原子类,以AtomicInteger为例,它的内部提供了诸多原子操作的方法。如原子替换整数值、增加指定的值、加1,这些方法的底层便是采用操作系统提供的CAS原子指令来实现的。
  • 对于AQS,在向同步队列的尾部追加节点时,它首先会以CAS的方式尝试一次,如果失败则进入自旋状态(在队列中自旋),并反复以CAS的方式进行尝试。此外,在以共享方式释放同步状态时,它也是以CAS方式对同步状态进行修改的。
  • 对于并发容器,以ConcurrentHashMap为例,它的内部多次使用了CAS操作。
    • 在初始化数组时,它会以CAS的方式修改初始化状态,避免多个线程同时进行初始化。
    • 在执行put方法初始化头节点时,它会以CAS的方式将初始化好的头节点设置到指定槽的首位,避免多个线程同时设置头节点。在数组扩容时,每个线程会以CAS方式修改任务序列号来争抢扩容任务,避免和其他线程产生冲突。
    • 在执行get方法时,它会以CAS的方式获取头指定槽的头节点,避免其他线程同时对头节点做出修改。

CAS的实现离不开操作系统原子指令的支持,Java中对原子指令封装的方法集中在Unsafe类中,包括:原子替换引用类型、原子替换int型整数、原子替换long型整数。这些方法都有四个参数:var1、var2、var4、var5,其中var1代表要操作的对象,var2代表要替换的成员变量,var4代表期望的值,var5代表更新的值。


http://www.ppmy.cn/news/355869.html

相关文章

3D动态视频屏保热带鱼水族馆

下载地址 https://download.csdn.net/download/file_data/12113080 简介 3d热带鱼水族箱屏保中文版是受到微软推荐的水族箱屏保,现在这个屏幕保护程序已被收录进了微软的 WinXP Plus Pack 当中。最新版本不仅修复了先前版本中存在的一些不足&#xff0c;而且又增加了新品种鱼…

Google宽松的工作环境

Google 在世界各地的办公室经常是很多人羡慕的对象&#xff0c;而这类话题早已泛滥&#xff0c;不过&#xff0c;下面这些来自 Google 加州 Mountain View 和瑞士苏黎世的办公室的图片还是让人免不了感慨一番&#xff0c;不得不承认&#xff0c;创造力来自宽松和自由的环境。 吃…

10大桌面辅助软件

整天面对Windows那枯燥不变的桌面&#xff0c;是不是已经感觉到厌烦了呢&#xff1f;快试试各种桌面辅助软件吧&#xff0c;它们不但功能强大&#xff0c;还可以让你的桌面变得与众不同。这里给大家来推荐几款广受欢迎的桌面辅助软件&#xff0c;希望大家喜欢。 桌面天气秀——…

ubuntu 12.04 3D 效果

[2012年04月29日更新] Ubuntu Linux 3D桌面完全教程&#xff0c;显卡驱动安装方法&#xff0c;compiz特效介绍&#xff0c;常见问题解答。 本教程最早是 一善鱼 YQ-YSY 于2008年编写并发布在Ubuntu中文论坛forum.ubuntu.org.cn配置美化区3D桌面特效版块的&#xff1a; ——…

ubuntu12.04 3D效果

2019独角兽企业重金招聘Python工程师标准>>> 不 推荐在Ubuntu 12.10版使用3D桌面特效&#xff0c;因为这个版本正在做较大的修改和测试&#xff0c;把 Unity 2D桌面取消了&#xff0c;这是为了在以后“统一”桌面做准备&#xff0c;所以在Ubuntu 12.10版中使用带3D特…

首款Intel双核挑衅四核 联想K900评测

为什么80%的码农都做不了架构师&#xff1f;>>> 文/毛文婷&#xff0c;邱鑫&#xff0c;汪洋 2GHz双核联想K900终于登场 今年1月的CES2013对于手机爱好者来说并没有带来太多惊喜&#xff0c;但即便是一场平平淡淡的展会&#xff0c;也依旧掀起了小小的波澜。虽然…

Ubuntu Linux 3D桌面完全教程,显卡驱动安装方法,compiz特效介绍,常见问题解答

本教程最早是 一善鱼 YQ-YSY 于2008年编写并发布在Ubuntu中文论坛forum.ubuntu.org.cn配置美化区3D桌面特效版块的&#xff1a; ——《图解Ubuntu8.10和8.04最简便的显卡驱动安装和3D桌面启用方法&#xff0c;compiz fusion常见问题解答》&#xff1b; 之后随着Ubuntu版本的…

Ubuntu 12.04 使用基本配置

目录: (无序) 1,传统界面的切换. 2,3D特效和相关显卡驱动安装和一些其他桌面布局设置. 3,任务栏删除移动添加等. 4,开机屏幕亮度 5,web,php服务器部署 6,用户目录英文化. 7,无法下载 bzip2的解决方案 8,开启root登陆 9,安装gtk库 10,开启休眠 11,安装屏保 12,开机自动mount分区…