CAS
大约 2 分钟
简介
含义:compare and swap,比较并交换,是一种用于在多线程环境下实现同步功能的机制。
CAS 操作包含三个操作数 -- 内存位置、预期数值和新值。
CAS 的实现逻辑是将内存位置处的数值与预期数值想比较,若相等,则将内存位置处的值替换为新值。若不相等,则不做任何操作。
java中CAS底层都是通过Unsafe类实现的。
AtomicInteger的compareAndSet方法源码
public final boolean compareAndSet(int expect, int update) { // 通过调用底层的unsafe方法实现 return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
java.util.concurrent.atomic包里的类都是基于CAS
cas运用
ABA问题

A线程首先读取余额为100
B线程读取余额100,花费50,借款50
此时余额还是100
A线程花费100,最后余额为0
此时A线程花费的100中,是存在B线程借款的50,并不是原始的100,就造成了ABA的问题。
ABA问题大多数情况是不会影响业务的,但如果涉及到账务,库存等,需要根据业务考虑。
解决方法
- 版本号,新增一个版本号属性,在比较值时加上版本号的判断。每次更新将版本号也更新,就避免了ABA的问题
- 邮戳,类似于版本号的操作,也是在比较值时加上戳记的判断。更新值同时也更新戳记
简单运用
1. 避免超售
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(10);
ThreadPoolExecutor executor = new ThreadPoolExecutor(20, 20, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(20), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i < 20; i++) {
executor.execute(() -> {
while (true) {
int remain = atomicInteger.get();
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (remain > 0) {
boolean b = atomicInteger.compareAndSet(remain, remain - 1);
if (b) {
System.out.println(Thread.currentThread() + "\t抢到票了!!");
break;
}
} else {
System.out.println(Thread.currentThread() + "\t没抢到。。。。");
break;
}
}
});
}
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("剩余票数:" + atomicInteger.get());
}
2. 简单的自旋锁
public class CASDemo {
static AtomicBoolean atomicBoolean = new AtomicBoolean(false);
private void lock() {
while (true) {
if (atomicBoolean.compareAndSet(false, true)) {
break;
}
}
}
private void unLock() {
atomicBoolean.compareAndSet(true, false);
}
public static void main(String[] args) throws Exception {
CASDemo cas = new CASDemo();
new Thread(() -> {
try {
cas.lock();
System.out.println(Thread.currentThread() + "\tget lock");
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread() + "\trelease lock");
cas.unLock();
}
}, "A").start();
TimeUnit.SECONDS.sleep(1);
new Thread(() -> {
try {
cas.lock();
System.out.println(Thread.currentThread() + "\tget lock");
} finally {
System.out.println(Thread.currentThread() + "\trelease lock");
cas.unLock();
}
}, "B").start();
}
}