这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战
源码分析
CAS的底层原理是基于自旋操作和Unsafe类的
class MyTask2 {
static AtomicInteger num = new AtomicInteger(5);
public static void main(String[] args) {
num.getAndIncrement();
}
}
先来看下getAndIncrement方法的源码
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();
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
}
可以看到返回的是unsafe类的实例调用的getAndAddInt方法
其中
- this:当前对象
- valueOffset:内存偏移量,Unsafe根据内存偏移地址获取数据
- 1:表示每次增加的值
再点击进去发现我们来到了Unsafe类,其中的很多方法都是用native修饰的,说明Unsafe类中的方法都直接调用操作系统底层资源执行响应的任务
public native void park(boolean var1, long var2);
public native int getLoadAverage(double[] var1, int var2);
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
主要看下do while循环的过程
- 首先var5 = this.getIntVolatile(var1, var2);中var1是当前变量,var2表示内存的偏移量
- while中的compareAndSwapInt尝试修改var5的值,该方法通过var1和var5获取变量对的值,如果这个值和var5不一样,说明已经有其他线程修改了var1+var2地址处的值,此时该方法返回false由于前面有个!号就继续循环。
- 如果这个值和var5一样,说明没有被其他线程修改,可以将var1+var2处的值修改为var5+var4,返回true直接退出循环
由于compareAndSwapInt是原子操作,所以修改var1+var2地址处的值的时候可以保证线程安全,
可以看到没有修改成功就一直在while循环中一直尝试,这就是自选锁的实现思想,相比于synchronized它在保障线程安全性的同时还保证了并发性,是线程不断尝试而不是锁住一块代码不让其他线程进来。
Unsafe
它是CAS的核心类,我们知道Java方法是无法访问底层系统,需要通过本地(native)方法访问,Unsafe类相当于提供了一个后门,通过这个类可以直接操作特定内存的数据。
再来看下Unsafe类的位置
package sun.misc;
public final class Unsafe {
}
可以看到它位于sum.misc包中,内部的方法可以像C语言的指针一样直接操作内存,因为Java中CAS操作的执行依赖于Unsafe类的方法
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;
变量value用volatile修饰,保证了多线程情况下数据的可见性。
CAS的全称为Compare-And-Swap,它是一条CPU并发原语,可以判断内存某个位置的值是否为预期值,如果是则更改为新值,这个过程是原子的
CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法,JVM会帮我们是实现出CAS汇编指令。这是一种完全依赖于硬件的操作,通过它实现原子操作。
原语
由于CAS是一种系统原语,属于操作系统用语的范畴,是由若干条指令组成,用于完成某个功能的过程,并且原语的执行必须是连续的,在执行过程中不允许被中断,也就是是CAS是一条CPU的原子指令,不会造成数据不一致的问题。