一、什么是 CAS

CAS 全程 Compare-And-Swap,它的功能是判断内存中某个位置的值是否为预期值,如果是则更改为新值,这个过程是原子性的。

二、CAS 作用

CAS 是另一个无锁解决方案,更准确的是采用乐观锁技术,实现线程安全的问题。

三、CAS 实现原理

在设计思想上,CAS 的执行有三个操作数,V-内存值,A-期望值,B-修改成的目标值。当且仅当 V 的值等于 A 时, CAS 通过原子方式用新值 B 来更新 V 的值,否则不会执行任何操作。

CAS 是一条CPU 并发原语。即属于操作系统用语范畴,是由若干条指令组成,用于完成某种功能的一个过程,并且原语的执行必须是连续的,在执行过程中不允许被中断。

JAVA 中的 CAS 操作通过调用 sun.misc.Unsafe 类中各个方法来实现。当调用 UnSafe 类中的 CAS 方法,JVM 会帮我们实现出 CAS 汇编指令。这是一种完全依赖与硬件的功能,通过它实现了原子操作。

四、实战演练

在 java.util.concurrent.atomic 包下提供了很多 AtomicXXX 原子性操作的类,它们底层封装了 UnSafe 类来提供 CAS 操作相关的方法,我们以 AtomicInteger 为例,执行 1000 的数字累加操作:

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
public class CASTest {

private static int number = 0;

private static AtomicInteger atomicNumber = new AtomicInteger(0);

public static void main(String[] args) throws Exception {
ExecutorService service = Executors.newCachedThreadPool();
test1(service);
test2(service);
service.shutdown();
}

private static void test1(ExecutorService service) throws Exception {
for (int i = 0; i < 1000; i++) {
service.submit(() -> {
number++;
});
}

TimeUnit.SECONDS.sleep(2);
System.out.println("number: " + number);
}

private static void test2(ExecutorService service) throws Exception {
for (int i = 0; i < 1000; i++) {
service.submit(() -> {
atomicNumber.incrementAndGet();
});
}

TimeUnit.SECONDS.sleep(2);
System.out.println("atomicNumber: " + atomicNumber.get());
}
}

执行结果:

1
2
number: 972
atomicNumber: 1000

案例中,我们声明了普通的静态变量 number 和原子类型的 atomicNumber,让它们在多线程下各执行 1000 次的数值累加。

由于 number ++ 编译成字节码是多行指令,并非原子操作,当多线程访问修改时,大概率有旧值覆盖新值的问题,因此出现累加 1000 次的操作后结果却小于 1000 的情况。

而 atomicNumber.incrementAndGet() 底层使用了 CAS,确保数值修改的原子性,因此能正常返回结果。