CAS和自旋锁

article/2025/8/20 15:36:13

什么是CAS

CAS算法Compare And Swap),即比较并替换,是一种实现并发编程时常用到的算法,Java并发包中的很多类都使用了CAS算法。

CAS算法有3个基本操作数:

  • 内存地址V
  • 旧的预期值A
  • 要修改的新值B

CAS使用自旋的方式来交换值,操作步骤为:

  1. 读取内存地址V的值保存在A中
  2. 在原子操作中比较内存地址V的值是否与A相同
  3. 相同时,修改内存地址V的值为B,原子操作成功。
  4. 不相同时,循环执行第一至第三步(自旋),直到成功。

什么是自旋锁?

自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。

对于互斥锁,会让没有得到锁资源的线程进入BLOCKED状态,而后在争夺到锁资源后恢复为RUNNABLE状态,这个过程中涉及到操作系统用户模式和内核模式的转换,代价比较高。但是自旋锁不会引起调用者堵塞,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁。

自旋锁的实现基础是CAS算法机制。CAS自旋锁属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新。

基于CAS实现-原子操作类

先看一个线程不安全的例子:

public class AutomicDemo {public static int num = 0;public static void main(String[] args){for(int i=0; i<5; i++){new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}for(int j=0; j<200; j++){num++;}}}).start();}/* 主控失眠3秒,保证所有线程执行完成 */try {Thread.sleep(15000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("num=" + num);}
}

输出结果:

num=950

因为自增操作不是原子性,多线程环境下,访问共享变量线程不安全。

解决方法,加synchronized同步锁:

for(int j=0; j<200; j++){synchronized (AutomicDemo.class){num++;}
}输出结果:
num=1000

线程安全。

synchronized确保了线程安全,但会让没有得到锁资源的线程进入BLOCKED状态,而后在争夺到锁资源后恢复为RUNNABLE状态,这个过程中涉及到操作系统用户模式和内核模式的转换,代价比较高。

尽管Java1.6为Synchronized做了优化,增加了从偏向锁到轻量级锁再到重量级锁的过度,但是在最终转变为重量级锁之后,性能仍然较低。

于是JDK提供了一系列原子操作类:AtomicIntegerAtomicLongAtomicBooleanAtomicReference等,它们都是基于CAS去实现的,下面我们就来详细看一看原子操作类。

public class AutomicDemo {public static AtomicInteger num = new AtomicInteger(0);public static void main(String[] args){for(int i=0; i<5; i++){new Thread(new Runnable() {@Overridepublic void run() {try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}for(int j=0; j<200; j++){num.incrementAndGet();}}}).start();}/* 主控失眠3秒,保证所有线程执行完成 */try {Thread.sleep(15000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("num=" + num.get());}
}输出:
num=1000

使用AtomicInteger之后,最终的输出结果同样可以保证是200。并且在某些情况下,代码的性能会比Synchronized更好。

原子类操作方法

  • public final int get():取得当前值
  • public final void set(int newValue):设置当前值
  • public final int getAndSet(int newValue):设置新值并返回旧值
  • public final boolean compareAndSet(int expect, int update):如果当前值为expect,则设置为update
  • public final boolean weakCompareAndSet(int expect, int update):如果当前值为expect,则设置为update,可能失败,不提供保障
  • public final int getAndIncrement():当前值加1,返回旧值
  • public final int getAndDecrement():当前值减1,返回旧值
  • public final int getAndAdd(int delta):当前值加delta,返回旧值
  • public final int incrementAndGet():当前值加1,返回新值
  • public final int decrementAndGet():当前值减1,返回新值
  • public final int addAndGet(int delta):当前值加delta,返回新值

AtomicInteger底层原理

所有Atomic相关类的实现都是通过CAS(Compare And Swap)去实现的,它是一种乐观锁的实现。

CAS实现放在 Unsafe 这个类中的,其内部大部分是native方法。这个类是不允许更改的,而且也不建议开发者调用,它只是用于JDK内部调用,看名字就知道它是不安全的,因为它是直接操作内存,稍不注意就可能把内存写崩。

Unsafe类使Java拥有了像C语言的指针一样操作内存空间的能力。有一下特点:
1、不受jvm管理,也就意味着无法被GC,需要我们手动GC,稍有不慎就会出现内存泄漏。
2、Unsafe的不少方法中必须提供原始地址(内存地址)和被替换对象的地址,偏移量要自己计算,一旦出现问题就是JVM崩溃级别的异常,会导致整个JVM实例崩溃,表现为应用程序直接crash掉。
3、直接操作内存,也意味着其速度更快,在高并发的条件之下能够很好地提高效率。

去看AtomicInteger的内部实现可以发现,全是调用的Unsafe类中的方法:

原子操作类AtomicInteger详解

该文章详细介绍所有原子类,后续有时间整理学习

https://blog.csdn.net/fanrenxiang/article/details/80623884

CAS机制ABA问题

ABA问题:CAS算法通过比较变量值是否相同来修改变量值以保证原子性,但如果一个内存地址的值本身是A,线程1准备修改为C。在这期间,线程2将值修改为B,线程3将值修改为A,线程1获取内存地址的值还是A,故修改为C成功。但获取的A已不再是最开始那一个A。这就是经典的ABA问题,A已不再是A。

如果解决ABA问题呢?

两个方法,1、增加版本号;2、增加时间戳。

  • 增加版本号:让值的修改从A-B-A-C变为1A-2B-3A-4C;这样在线程1 中就能判别出1A不是当前内存中的3A,从而不会更新变量为4C。
  • 增加时间戳:值被修改时,除了更新数据本身外,还必须更新时间戳。对象值以及时间戳都必须满足期望,写入才会成功。JDK提供了一个带有时间戳的CAS操作类AtomicStampedeReference。

CAS的缺点

Java原子类使用自旋的方式来处理每次比较不相同时后的重试操作,下面来看看AtomicInteger类incrementAndGet方法的代码:

//AtomicInteger 的incrementAndGet方法,变量U为静态常量jdk.internal.misc.Unsafe类型
public final int incrementAndGet() {//使用getAndAddInt方法,实际操作类似j++return U.getAndAddInt(this, VALUE, 1) + 1;
}
//jdk.internal.misc.Unsafe类型的getAndAddInt方法
public final int getAndAddInt(Object o, long offset, int delta) {int v;do {//获取变量o的可见值v = getIntVolatile(o, offset);//比较与替换变量o(CAS算法)} while (!weakCompareAndSetInt(o, offset, v, v + delta));return v;
}
//jdk.internal.misc.Unsafe类型的weakCompareAndSetInt方法
public final boolean weakCompareAndSetInt(Object o, long offset,int expected,int x) {//执行比较与替换return compareAndSetInt(o, offset, expected, x);
}

在Unsafe类的getAndAddInt方法中使用了do…while循环语句,循环语句的作用为自旋,Unsafe的weakCompareAndSetInt实现CAS算法。

如果weakCompareAndSetInt一直不成功将会一直自旋下去,这将消耗过多的CPU时间。而且原子类使用CAS算法实现,这导致原子类只能保证一个变量的原子操作,对于需要保证一个具有多个操作的事务将变得无能为力。

总结如下:

  1. CPU开销较大
    在并发量比较高的情况下,如果许多线程反复尝试更新某一个变量,却又一直更新不成功,循环往复,自旋会给CPU带来很大的压力
  2. 不能保证代码块的原子性
    CAS机制所保证的只是一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用Synchronized了。

为应对CAS存在缺点,替换方案如下:

  1. 自旋效率:Java提供了自适应自旋锁
  2. 片面性:应对片面性问题Java提供了读写锁
  3. ABA问题:用AtomicStampedReference/AtomicMarkableReference解决ABA问题

参考文章:
漫画:什么是 CAS 机制?
知识点: JAVA 悲观锁与乐观锁原理分析 ABA与自旋效率问题分析及解决


http://chatgpt.dhexx.cn/article/wLNtx07Q.shtml

相关文章

Java中的自旋锁,手动实现一个自旋锁

自旋锁 CAS是实现自旋锁的基础,CAS利用CPU指令保证了操作的原子性,已达到锁的效果。自旋是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁, 当线程发现锁被占用时,会不断循环判断锁的状态,直到获取。这样的好处是减少线程上下文切换的消耗,缺点是循环…

学习自旋电子学的笔记04:模拟自旋波在弯曲磁畴壁中传播

文章目录 前言零、笔记03中错误的补充改正1.保持电子的极化方向不变的原因2.Oxs_SpinXferEvolve类的额外补充说明3.时间演化器的时间步长相关补充说明 一、文章概述和要复现的微磁模拟1.文章概述2.要复现的微磁模拟 二、FIG.1三、 FIG.21. FIG.2(a-b)2. FIG.2(c-f) 四、 FIG.3五…

CAS和自旋到底是一个概念吗?

问题: CAS是 compare and swap ,就是一个比较工作内存和主内存的值是否相同&#xff0c;相同的话&#xff0c;就用新值来替换这么一个操作。 但是&#xff0c;为什么好多地方都说这是自旋呢&#xff1f; 我理解比较一次的话&#xff0c;成功就返回true了&#xff0c;失败&am…

CAS及CAS自旋

1. CAS简介 比较并交换(compare and swap, CAS)&#xff0c;是原子操作的一种。在多线程没有锁的状态下&#xff0c;可以保证多个线程对同一个值的更新。 CAS可用于在多线程编程中实现不被打断的数据交换操作&#xff0c;从而避免多线程同时改写某一数据时由于执行顺序不确定…

自旋玻璃(spin glass)、自旋冰(spin ice)和量子自旋液体(quantum spin liquid)(之二)

文章目录 13. 几何阻挫&#xff08;Geometrical frustration&#xff09;13.1 磁序&#xff08;Magnetic ordering&#xff09;13.2 数学定义13.3 水冰&#xff08;water ice&#xff09;13.4 Paulings model 的扩展&#xff1a;广义的阻挫13.5 人工几何阻挫铁磁体13.6 没有晶格…

CAS自旋

文章目录 1. CAS简介2. CAS的特点3. 自旋–比较和交换4. 什么是ABA问题5. ABA问题怎么解决6. 悲观锁7. 乐观锁8. CAS锁升级 CAS面试提问环节&#xff1a; synchronized、ReentrantLock、CAS全家桶发售HashMap、Hashtable、ConcurrentHashMap组合拳出击 1. CAS简介 比较并交换(…

自旋玻璃(spin glass)、自旋冰(spin ice)和量子自旋液体(quantum spin liquid)(之一)

文章目录 1. Giorgio Parisi 简介2. 复杂无序系统2.1 相变、序参量与对称性破缺2.2 复杂系统 3. 自旋玻璃简介3.1 自旋冻结3.2 亚稳态3.3 磁化弛豫3.4 玻璃化和无序系统3.5 Ising model3.6 自旋玻璃模型3.7 自旋玻璃相变 4. 磁场中的现象5. Edwards-Anderson model6. Sherringt…

office卸载工具怎么用(官方干净卸载方法)

https://jingyan.baidu.com/article/39810a23593f37b636fda60d.html

Office卸载安装问题

卸载Office 问题描述 此前已安装过新的Office&#xff0c;按照正常的卸载流程卸载后&#xff08;控制面板卸载后&#xff09;&#xff0c;安装新的Office时&#xff0c;提示&#xff1a; 无法安装64位版本的Office&#xff0c;因为在电脑上已有32位程序。如下图所示。 **解…

32位office卸载不干净怎么办如何删除32位Office

以前在电脑上安装了32位系统的Office&#xff0c;现在想要换成64位的Office&#xff0c;但是在安装的时候提示无法进行安装&#xff0c;需要先卸载以前的32位Office&#xff0c;出现这种情况怎么办呢&#xff1f;如何彻底卸载干净32位系统的Office呢&#xff1f;下面就一起来看…

Office卸载不干净,注册表项权限修改后仍然无法删除的问题

Office卸载不干净&#xff0c;注册表项权限修改后仍然无法删除的问题 针对卸载Office最极端的情况&#xff0c;试试以下方法。 1.卸载开始菜单的office; 可以借助以下工具进行进行清除&#xff0c;完全卸载&#xff08;但是对本人无效&#xff09; 链接&#xff1a;https://…

mac m1 office卸载重装(学校官方正版)

卸载office 1、应用程序中将所有的microsoft相关软件移到废纸篓&#xff0c;可能还有outlook或者onedrive&#xff0c;清空废纸篓 2、cmdshiftG&#xff0c;输入/Library/Preferences&#xff0c;删除所有com.Microsoft开头的文件 3、进入/Library/PrivilegedHelperTools&…

卸载32位office安装64位office卸载不完全导致不能安装64位office时解决办法

转载自https://blog.csdn.net/zzfenglin/article/details/60780831 问题描述 安装64位office办公软件的时候提示已经安装32位的office办公软件所以无法继续安装&#xff0c;但实际上之前安装的32位的office办公软件已经卸载了。问题现象截图如下&#xff1a; 解决办法 从问题描…

office2019 完美卸载

记录下我日常手贱的经历。 事情的起因呢&#xff0c;是在家办公的时候呢&#xff0c;突然要写一份文档&#xff0c;然后呢&#xff0c;我的wps过期了&#xff0c;只能看&#xff0c;而不能编辑。然后我就打算装个office2019感受下。结果我就卸载了原来安装过但是已经过期了的o…

Office uninstall(专业office卸载工具)绿色单文件版V1.8.3 全面清除office卸载残留

Office uninstall 是一款专门为微软Office办公软件量身定做的office卸载工具&#xff0c;可以帮助大家彻底卸载已经安装到电脑上的Office软件&#xff0c;彻底解决office卸载不干净&#xff0c;无法重新安装的问题&#xff0c;全面兼容office2003、office2007、office2010、off…

Win10预装Office卸载工具

联想软件远程服务&#xff0c;让您足不出户&#xff0c;轻松解决电脑问题&#xff01;软件调试、电脑加速、游戏加速、重装系统、修复浏览器、安装驱动&#xff0c;远程or上门&#xff1f;联想专家一对一服务任您挑选&#xff01; 重要提示&#xff1a;您需要在电脑端下载并运行…

将office办公软件彻底从计算机中卸载清除

此情况针对于计算机系统中可能包含两个版本或更多的office同时存在导致在新建各种文档时会有些冲突&#xff0c;卸载不干净后&#xff0c;重新安装回去也还是无济于事&#xff0c;所以经过探索&#xff0c;将成功的方法发布给大家&#xff0c;希望对大家有所帮助。 有两种用于彻…

关于机器学习SVM中KKT条件的深入理解推导

关于机器学习SVM中KKT条件的深入理解推导 目前为止的已知KKT条件违反KKT条件的情况参考文献 本文面向在寻找KKT条件相关推到文章的读者&#xff0c;且默认前面关于svm的松弛下的模型和smo算法推到都已经了解。如果没有或者需要温习&#xff0c;请参看支持向量机SVM与SMO算法的的…

理解KKT条件

一、引言 对于无约束最优化问题&#xff0c;其搜索空间是无界的&#xff0c;只要确定了搜索方向和步长因子&#xff0c;便可以在一轮或几轮迭代之后找到最优解或近似最有解。这里举个不太恰当的例子&#xff0c;无约束最优化如同在浩瀚的宇宙中寻找体积最大的星球&#xff0c;你…

KKT条件(卡罗需-库恩-塔克条件)

1&#xff0c;定义 KKT是啥&#xff1f; 它是Karush、Kuhn和Tucker三个人。这三个人单独提出了在非线性规划中获得最优解的必要条件。 看着很复杂呀&#xff1f; 还好啦。。。只是将拉格朗日乘数法中的等式约束条件泛化到了不等式。 2&#xff0c;先来几个简单例子 为什么要…