一、何为CAS操作
Java中的CAS操作,即Compare-And-Swap,是一种用于实现无锁编程的原子操作。在Java的java.util.concurrent.atomic
包中,许多原子类都利用了CAS操作来保证复合操作的原子性。在底层操作系统层面,CAS通常由特定的CPU指令实现,这些指令能够检测内存中的值是否为预期值,并在条件满足的情况下,将其更新为新值。
二、操作系统层面的CAS实现原理
- 原子性- - 现代处理器通常提供了原子指令,这些指令可以保证操作在单个CPU周期内完成,从而避免多线程环境下的数据竞争。
-2. 内存屏障- - 为了确保操作的内存可见性,CAS操作可能伴随着内存屏障(Memory Barrier)的使用。内存屏障可以防止编译器和处理器对指令进行重排序,确保在CAS操作之前和之后的指令按正确的顺序执行。
-3. CPU指令- - 不同处理器架构可能有不同原子指令实现CAS,例如x86架构的LOCK CMPXCHG
指令(处理器自动锁定总线,防止其他cpu对共享变量的访问),或在其他架构上的LR (Load-Reserved)
和SC (Store-Conditional)
指令对。
-4. 自旋锁- - 当CAS操作失败时(即内存值在读取和尝试写入之间被其他线程修改),线程通常会进入自旋状态,重复尝试执行CAS操作,直到成功为止。这种机制称为自旋锁。
-5. 无锁算法- - CAS操作是实现无锁算法的基础,它允许开发者构建不依赖于传统锁机制的并发算法,从而提高性能并减少死锁的可能性。
三、ABA问题
由于CAS只能检查内存地址中的值是否发生了变化,如果一个值从A变为B,再变回A,CAS将无法检测到这种变化,这就是所谓的ABA问题。
为了解决这个问题,Java中的AtomicStampedReference
类使用版本号(Stamp)确保操作的安全性。
-### Java中的CAS实现示例–在Java中,java.util.concurrent.atomic.AtomicInteger
类中的compareAndSet
方法就是一个典型的CAS操作
public class AtomicInteger extends Number implements Serializable {private static final long serialVersionUID = 6214790243576657955L;private final sun.misc.Unsafe unsafe;private final long valueOffset;...public boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);}...
}
-在这个示例中,compareAndSwapInt
方法是由sun.misc.Unsafe
类提供的,它是Java中用于执行低级别操作的工具类。compareAndSwapInt
方法尝试将当前对象中偏移量为valueOffset
的整数字段中的值替换为新值,如果当前值为期望值(expect
),则返回true
;否则,不做更改,返回false
。
四、AtomicStampedReference与AtomicReference的区别:
AtomicStampedReference它内部不仅维护了对象值,还维护了一个时间戳(我这里把它称为时间戳,实际上它可以使任何一个整数,它使用整数来表示状态值)。
当AtomicStampedReference对应的数值被修改时,除了更新数据本身外,还必须要更新时间戳。当AtomicStampedReference设置对象值时,对象值以及时间戳都必须满足期望值,写入才会成功。
因此,即使对象值被反复读写,写回原值,只要时间戳发生变化,就能防止不恰当的写入。
五、总结
CAS操作在多线程环境中非常有用,因为它可以减少锁的使用,提高程序的并发性能。然而,开发者在使用CAS时也需要注意ABA问题和自旋锁可能带来的性能问题。