目录
1、CAS是什么
2、CAS的生活化例子
3、Java中的atomic包
4、unsafe类
5、CAS的缺点及解决方案
小结
1、CAS是什么
CAS(Compare and Swap)是一种并发编程中的原子操作,用于实现多线程环境下的无锁同步。它是一种乐观锁的实现方式,通过基于硬件的原子指令,同时完成“读内存,比较是否相等,修改内存”这三个步骤,本质上需要CPU指令的支持。通过比较内存中的值与预期值是否相等来决定是否进行更新。
在Java中,CAS通常使用java.util.concurrent.atomic包下的原子类来实现,例如AtomicInteger、AtomicLong、AtomicReference等。这些原子类提供了一系列的原子操作方法,保证了操作的原子性和线程安全性。
CAS操作包含三个操作数:内存位置(通常是一个变量)、预期值和新值。
它的执行过程如下:
1. 获取当前内存位置的值(旧值)。
2. 比较内存位置的值与预期值是否相等,如果相等则执行第4步,否则(不相等)执行第3步。
3. 更新内存位置的值为新值,然后返回到第1步。
4. CAS操作成功,更新成功;否则,返回到第1步。
CAS实现流程图:
图片来源:【Java中的CAS实现原理】_java cas原理_浪打白龙的博客-CSDN博客
2、CAS的生活化例子
例如:常见的修改登录密码操作。我们无论是在各个APP还是网站上修改密码的时候,一般都需要输入用户id、原密码和新密码等诸如此类的方式。那么在这个修改操作过程中,需要的是:数据库存储的原密码信息、用户输入的原密码、以及更新后的新密码。数据存储的原密码信息(主内存),用户输入的原密码(线程副本)。当数据库中存储的和用户输入的原密码对比相同的时候,才可以将原密码更新为新密码,否则就不能更新。
简单理解就是:线程M(工作区值为A),认为主内存V中的共享变量值是(包含)A,如果V的值是A,那么就将新值B替换V。如果不是,就不更新V的值,只要告诉我V的最新值。线程M会一直自旋操作。
3、Java中的atomic包
Java中的java.util.concurrent.atomic包提供了一组原子类,用于支持在多线程环境下进行原子操作。这些原子类提供了一些基本类型的原子操作,如整型、长整型、布尔型等,以及一些引用类型的原子操作。
以下是java.util.concurrent.atomic包中常用的原子类:
AtomicBoolean:提供了对布尔类型的原子操作,支持原子的读取和设置操作。
AtomicInteger:提供了对整型的原子操作,支持原子的读取、设置、自增、自减等操作。
AtomicLong:提供了对长整型的原子操作,支持原子的读取、设置、自增、自减等操作。
AtomicReference:提供了对引用类型的原子操作,支持原子的读取和设置操作。
AtomicIntegerArray:提供了对整型数组的原子操作,支持原子的读取、设置、自增、自减等操作。
AtomicLongArray:提供了对长整型数组的原子操作,支持原子的读取、设置、自增、自减等操作。
AtomicReferenceArray:提供了对引用类型数组的原子操作,支持原子的读取和设置操作。
这些原子类的操作都是线程安全的,可以在多线程环境下进行并发访问而不需要额外的同步措施(如锁)。它们使用了底层的硬件原子指令或其他技术来实现原子性,从而提供了高效且线程安全的操作。 通过使用atomic包中的原子类,可以简化多线程编程的复杂性,并提高程序的性能和可靠性。它们通常用于实现计数器、标志位、状态标记等在多线程环境下的共享变量。
4、unsafe类
(1)CAS的原子性靠的是底层的unsafe类,这个类中文译为“不安全的”,因此对于普通程序员来说是“有风险”的,一般应用开发者不会用到这个类。
(2)unsafe类是CAS的核心类,由于java无法直接访问底层系统,需要通过本地(native)方法访问,通过该类可以直接操作特定的内存数据。unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为java中的CAS依赖于unsafe类中的方法。
(3)注意unsafe类中的所有方法都是native修饰的,也就是说unsafe类中的方法都直接调用操作系统底层资源执行相应任务,unsafe类中的native方法是调用底层原语,原语是有原子性的。
5、CAS的缺点及解决方案
ABA问题,即在操作过程中,内存位置的值可能经过多次修改后又恢复成原来的值。
解决方案:可以添加版本号作为标识,在CAS比较数据当前值和旧值的时候,也要比较版本号是否符合预期,从而对应地增加版本号。如果当前版本号和读到的版本号一致,则修改数据,版本号+1; 如果当前版本号高于读到的版本号,就操作失败。
Java提供了AtomicStampedReference和AtomicMarkableReference等类来解决ABA问题。
只能保证一个共享变量的原子操作
JDK1.5之后,新增了AtomicReference类来保证引用对象之间的原子性,可以将多个变量放到一个对象中。
循环时间长开销大,高并发多个线程同时操作的情况下,可能会导致大量线程CAS失败,CAS保持长时间自旋,加重CPU的执行负担,降低了并发能力。
解决方案: 1. 分散操作热点,使用 LongAdder 替代基础原子类 AtomicLong。2. 使用队列削峰,将发生 CAS 争用的线程加入一个队列中排队,降低 CAS 争用的激烈程度。例如JUC 中非常重要的基础类 AQS(抽象队列同步器)。
小结
总之,CAS是一种无锁同步的机制,通过比较并交换的方式来实现多线程环境下的原子操作。它避免了使用锁的开销,提供了一种高效的并发同步方式。
参考:
【精选】Java中CAS详解_java cas-CSDN博客
https://www.cnblogs.com/Shuuichi/p/10590710.html
并发编程之CAS&Atomic原子操作 - 知乎
【精选】Java多线程 (三)—— CAS机制_java中的cas机制_有你的星空的博客-CSDN博客
https://baijiahao.baidu.com/s?id=1662010083512305225&wfr=spider&for=pc
Java中的CAS_java cas用法-CSDN博客
感谢阅读,码字不易,多谢点赞!如有不当之处,欢迎反馈指出,感谢!