导航
-
-
- 一. 简介
- 二. CAS原子操作原理
- 三. CAS 实现自旋锁
- 四. CAS原子操作的优点
-
- 1. 非阻塞
- 2. 原子性
- 3. 高效性
- 五. CAS原子操作的缺点
-
- 1. ABA问题
- 2. 自旋开销
- 3. 只能保证一个共享变量的原子性操作
-
一. 简介
CAS是"比较并交换
"(Compare and Swap)的缩写。是一种并发控制机制
,用于实现多个线程同时对同一数据进行原子操作
(读取、写入、更新),并且能够保证操作的一致性。
# 1.比较:通过比较当前值和期望值是否一致来判断是否修改成功
# 2.交换:如果一致则修改,否则重新尝试。
CAS的一种常见应用是实现乐观锁
。在乐观锁机制中,线程在更新共享变量之前先检查该变量是否被其他线程修改过,如果没有修改则更新,否则重新尝试。CAS操作正是基于这个原理来实现的。
二. CAS原子操作原理
每一个 CAS 操作过程都包含三个运算符: 一个内存地址V,一个期望值A和一个新值B
# 1.通过内存地址V, 读取当前值 ,并将当前值设置为期望值 A
# 2.执行修改操作,得到一个新值B
# 3.再次通过内存地址V, 读取当前值
# 4.将期望值A与当前值进行比较, 如果一致,将新值B写入内存
# 5.如果不一致, 说明内存当前值已经被其他线程修改, 重复上面的步骤再次尝试,直到写入操作完成
也就是说, 线程的一次CAS原子操作, 需要从内存中获取当前值两次,一次用于设置期望值,一次用于当前值与期望值的比较。
由于当前值和期望值比较, 可能会不一致, 那么当前线程的写入操作就会失败, 线程重新从内存读取当前值, 重新执行计算操作, 最后再次尝试CAS操作,直到执行成功为止, 这种循环重试的过程,被称为 "循环CAS
"或者 “自旋CAS
”。
三. CAS 实现自旋锁
自旋锁
是一种用于多线程编程中的同步机制
,它可以阻塞线程
直到自旋锁可用。当多个线程尝试同时访问共享资源时,只有一个线程能够获得自旋锁,其他线程会在自旋锁被释放前一直等待。与互斥锁不同的是,自旋锁在等待期间并不会阻塞线程,而是让线程不断执行一个忙等待的循环,直到自旋锁可用。
上文提到, 当前值与期望值不一致时, 会重复操作, 这种重复循环的操作是无限的, 直到执行成功为止。循环即自旋,所以CAS的比较
与替换
的循环原子操作, 可视为CAS实现自旋锁
的过程。
public class SpinLockExample {
private static AtomicInteger state = new AtomicInteger(0);
public static void main(String[] args) {
//创建5个线程,模拟并发
for (int i = 0; i < 5; i++) {
new Thread(() -> {
String name = Thread.currentThread().getName();
lock(name);
// 执行临界区代码
System.out.println(name + "-执行业务逻辑");
unlock(name);
}).start();
}
//保证所有线程都执行完
try {
Thread.sleep(100);