CAS

本文最后更新于:2022年6月11日 下午

CAS
CAS,Compare And Swap,即比较并交换。整个AQS同步组件(例:ReentrantLock获取锁的时候)、Atomic原子类操作等等都是以CAS实现的,ConcurrentHashMap在1.8的版本中也调整为了CAS+Synchronized。

定义

CAS是JUC的基础

在CAS中有三个参数:内存值V、旧的预期值A、要更新的值B,当且仅当内存值V的值等于旧的预期值A时才会将内存值V的值修改为B,否则什么都不干。

ReentrantLock获取锁(AtomicInteger也用到了):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//ReentrantLock内部类NonfairSync
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
//ReentrantLock内部类FairSync
final void lock() {
acquire(1);//最后调用了tryAcquire,内部用到了compareAndSetState(0, acquires)
}


//AbstractQueuedSynchronizer
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;//为变量值在内存中的偏移地址
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
//Unsafe
//该方法为本地方法,有四个参数,分别代表:对象、对象的地址、预期值、修改值
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

Unsafe是CAS的核心类,提供了硬件级的原子操作,Java无法直接访问底层操作系统,而是通过本地(native)方法来访问。

CAS的缺点

如果自旋CAS长时间地不成功,则会给CPU带来非常大的开销。在JUC中有些地方就限制了CAS自旋的次数,例如BlockingQueue的SynchronousQueue。也可以自己设置可以使用-XX:PreBlockSpin来更改。

ABA问题,解决方案则是版本号,Java提供了AtomicStampedReference来解决。Pair为AtomicStampedReference的内部类,主要用于记录引用和版本戳信息(标识)。版本戳为int型,保持自增。同时Pair是一个不可变对象,其所有属性全部定义为final,对外提供一个of方法,该方法返回一个新建的Pari对象。pair对象定义为volatile,保证多线程环境下的可见性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//ABA问题
ublic class Test {
private static AtomicInteger atomicInt = new AtomicInteger(100);
private static AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100, 0);

public static void main(String[] args) throws InterruptedException {
Thread intT1 = new Thread(new Runnable() {
@Override
public void run() {
atomicInt.compareAndSet(100, 101);
atomicInt.compareAndSet(101, 100);
}
});

Thread intT2 = new Thread(new Runnable() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
boolean c3 = atomicInt.compareAndSet(100, 101);
System.out.println(c3); // true
}
});

intT1.start();
intT2.start();
intT1.join();
intT2.join();

Thread refT1 = new Thread(new Runnable() {
@Override
public void run()
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
atomicStampedRef.compareAndSet(100, 101, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
atomicStampedRef.compareAndSet(101, 100, atomicStampedRef.getStamp(), atomicStampedRef.getStamp() + 1);
}
});

Thread refT2 = new Thread(new Runnable() {
@Override
public void run() {
int stamp = atomicStampedRef.getStamp();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
}
boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp + 1);
System.out.println(c3); // false
}
});

refT1.start();
refT2.start();
}
}

只能保证一个共享变量的原子操作,Java从1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,可以把多个变量放在一个对象里来进行CAS操作。


CAS
http://www.muzili.ren/2022/06/11/CAS/
作者
jievhaha
发布于
2022年6月11日
许可协议