面试官:JAVA的CAS 你知道吗(大厂面试的五杀)?
连续一系列的问题: CAS- - ->unsafe类 - - ->cas的底层思想 - - -> 是否会引发ABA问题- - -> 原子引用
什么是CAS?
java中有一个非常重要的关键字volatile,它有线程安全的一个轻量级控制能力,但是不能保证一定会线程安全.volatile关键字可以保证可见性(JMM内存计算模型中可见),禁止指令重排,但是不能保证原子性,所以在高并发操作的时候,还是可能会导致数据修改异常.
这个时候java的juc包中提供了一个原子操作类,可以保证原子性,一组操作保证它能不被其他线程串改.我们就拿AtomicInteger来说明.
这是官方给出的CAS操作方法:
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
/**
unsafe.compareAndSwapInt(this, valueOffset, expect, update);
this:代表当前原子操作类对象
valueOffset:内存偏移量,地址址
update:要操作的变量值
*/
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final int getAndSetInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var4));
return var5;
}
/**
compareAndSwapInt方法被native修饰,说明unsafe类底层调用的其他语言的接口,CAS原并发语句体现在java的sun.misc.Unsafe类中的各个方法,JVM会帮我们实现CAS汇编语言.一种完全依赖硬件的语言,通过它实现 原子操作.
*/
这里我们看到当采用了自旋锁,do{}while(){} 对于该方法的控制条件理解:var5 获取当下对象的数据值,然后与是否通知修改了内存值,如果已经修改则返回
unsafe类
unsafe类是CAS的核心类,它的所有方法都是native修饰的,因为java无法访问底层.UnSafe类在于sun.misc包中,其内部方法操作可以向C的指针一样直接操作内存,因为Java中CAS操作的助兴依赖于UNSafe类的方法.
变量ValueOffset,便是该变量在内存中的偏移地址,因为UnSafe就是根据内存偏移地址获取数据的
CAS的底层思想
ABA问题
什么是ABA问题呢? ABA问题其实很简单,看字面意思,就是A变量修改成B变量后再次修改为A变量,最后值一样,但是中间被修改了的问题.CAS思想比较并交换一定会带来ABA问题.
为什么?
cup在操作数据的时候会存在一定的时间差,而不同的线程会利用这个时间差来多次修改JMM中共享数据,虽然最后的值是一样的但是过程不一样,这就是ABA问题,偷偷做了一些不为人知的秘密.
比如: A线程从共享内存中取出变量a,放入自己内存修改,同时B线程也做同样操作,A由于某种原因挂起的时间长与B,B线程在这期间先把a改成b,在改成a,这时候compareAndSet方法检查不到其中的变化.
原子引用和带时间戳的原子引用
原子引用AtomicReference (ref);
带时间戳的:AtomicStampedReference(initialRef ,initalStamp);
原子引用功能让我们能够操作对象类型数据,带时间戳的目的就是为了版本控制防止ABA问题.
下面直接看代码
console:
100 共享内存值
101 2 修改后的内存值,和版本
100 3 修改后的内存值,和版本
3 当下时间版本
false
3 100
当版本不一致的时候,数据没有被修改返回false