Java CAS

tech2023-06-10  106

一、CAS是什么?

CAS是compare and swap的简称,从字面上理解就是比较并更新,这是一个实现并发算法时常用到的一种技术。简单来说:从某一个内存上取V,和预期值A进行比较,如果内存V和预期值A的结果相等,那么我们就把新值B更新到内存,如果不相等,那么就重复上述操作直到成功为止。这是一种实现并发算法时常用到的技术,是乐观锁(与之相对synchronized是一种悲观锁)。

二、原理

CAS操作需要有三个操作数:内存地址V,旧的预期值A,即将要更新的目标值B

CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作。

三、具体实现方式

Java中的Atomic系列就是使用CAS实现的,以下就以AtomicInteger为例来看下Java的实现过程:

3.1 属性

private static final long serialVersionUID = 6214790243416807050L; // setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); //内存偏移地址 private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } //变量用volatile修饰 private volatile int value;

3.2 自增

对当前的值自增1

public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }

这里调用了Unsafe的一个方法,实现如下:

public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2); } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }

这里又调用了本类的compareAndSwapInt方法:

public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

这个方法利用CAS操作,将var4和内存偏移到的变量进行对比,如果相等,那么将这个地址所代表的变量设置为var5,否则返回false

四、CAS优点和缺点

4.1 缺点

4.1.1 ABA问题

假如线程1从内存X中取出A,这时候另一线程2也从X中取出A,并且线程2进行了一些操作将内存X中的值变成了B,然后线程2又将内存X中的数据变成A,这时候线程1进行CAS操作发现内存X中仍然是A,然后线程1操作成功。虽然线程1的CAS操作成功,但是整个过程是有问题的。比如链表的头在变化了两次后恢复了原值,但是不代表链表没有发生变化。

所以JAVA中提供了AtomicStampedReference/AtomicMarkableReference来处理会发生ABA问题的场景,主要是在对象中额外再增加一个标记来标识对象是否有过变更。

4.1.2 循环时间开销很大

CAS通常是配合无限循环一起使用的,如果CAS失败,会一直进行尝试。如果CAS长时间一直不成功,可能会给CPU带来很大的开销。

4.1.3 只能保证一个变量的原子操作

当对一个变量进行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个变量进行操作时,CAS目前无法直接保证操作的原子性。但是我们可以通过以下两种方式来解决:

使用互斥锁来保证原子性将多个变量封装成对象,通过AtomicReference来保证原子性。

4.2 优点

和Synchronized相比,省去了挂起线程、恢复线程的开销,资源耗费少

最新回复(0)