CAS介绍
CAS全称是Compare And Swap,它的实现和它的字面意思一样,先比较后交换,它是CPU硬件层面的一种指令,从CPU层面能保证"比较并更新"这一段操作的原子性。
与synchronized关键字比较不同是synchronized是一种悲观锁,CAS是一种乐观锁。
悲观锁和乐观锁
- 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样当第二个线程想拿这个数据的时候,第二个线程会一直堵塞,直到第一个释放锁,他拿到锁后才可以访问。java中的synchronized关键字就是典型的悲观锁思想。
- 乐观锁:乐观锁概念为每次拿数据的时候都认为别的线程不会修改这个数据,所以不会上锁,但是在更新的时候会判断一下在此期间别的线程有没有修改过数据,乐观锁适用于读操作多的场景。在Java中java.util.concurrent.atomic包下面的原子变量就是使用了乐观锁的一种实现方式CAS实现。
CAS实现原理
CAS有三个操作数,内存值V,旧的预期值A,要修改的更新值B,当A和V相同时将V修改成B,否则一直比较V和的值直至A和V相同为止,使用java代码大致实现逻辑如下:
public class CasDemo {
private volatile Integer value;
public boolean compareAndSwap(int except, int update) {
if (value == except) {
this.value = update;
return true;
}
return false;
}
}
value使用volatile修饰,保证了该变量的有序性和可见性,compareAndSwap方法两个入参,一个期望值(except),一个更新值(update),先判断value是否和期望值相等,相等则修改为更新值返回true表示修改成功,不相等说明已经被其他线程修改了,返回false修改失败
JDK中的CAS的实现
在JDK中的java.util.concurrent(JUC java并发工具包)就是建立在CAS之上的,我们以并发原子类AtomicInteger为例,先看源码(源码比较多,所以只保留了重点部分)
public class AtomicInteger extends Number implements java.io.Serializable {
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); }
}
private volatile int value;
public final int get() {
return value;
}
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
getAndIncrement(),getAndDecrement()方法分别相当于 i++, i– 的操作,compareAndSet则是上面比较并交换操作,可以看到它们都是调用了Unsafe对象的方法。
再看看Unsafe的部分源码
可以看到Unsafe类的方法全部是用native修饰的,属于本地方法。
由于Java 方法无法直接访问底层,因此无法直接进行内存操作,所以在jdk中内存操作都封装在sun.misc.Unsafe类里面,Java通过本地方法去操作内存。
再看AtomicInteger的compareAndSet方法,是调用Unsafe的compareAndSwapInt方法
这里有四个入参,expect和update容易理解,分别是预期值和更新值,valueOffset通过源码可知它是通过Unsafe的objectFieldOffset本地方法返回的一个数值
因此,valueOffset大致可以理解为变量value在内存中的地址,Unsafe的compareAndSwapInt方法就是借助C来调用CPU底层,通过valueOffset获取value内存中的值并与expect比较,相同则更新为update返回true,不同则什么都不操作返回false。
java.util.concurrent包下的所有CAS操作都是基于这一系列原子操作.