CAS详解
title: JUC.CAS及原子类源码
CAS提纲
我觉得网上很多写CAS和原子类源码的都是垃圾。我不是在针对他们个人,而是真心觉得他们写的垃圾。所以没办法被迫写一篇博客给程序员同胞做贡献。
CAS简介
CAS的全称Compare And Swap,即比较并交换。在JAVA的并发工具类当中无处不在。如果说AQS是爸爸一般存在。那么CAS简直就是爷爷级的存在。
CAS与AQS是整个JUC基石。
在CAS中有三个参数:内存值V、旧的预期值A、要更新的值B,当且仅当内存值V的值等于旧的预期值A时才会将内存值V的值修改为B,否则什么都不干。
暴力拆迁CAS
看CAS最简单的方式就是拿一个JUC下原子类来玩一玩。所以咱们拿AtomicInteger这个类来玩。先看下类图
unsafe是CAS工具类,通过native方法调用底层。
valueOffset是当前类对象中使用变量的偏移量。
value原子类的值。
后面我具体上一个代码解释unsafe和valueOffset让明白的更彻底。咱们先看AtomicInteger中的getAndIncrement方法是怎么替换内容的。
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
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;
}
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSetInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) {
oop p = JNIHandles::resolve(obj);
jint* addr = (jint *)index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) == e;
} UNSAFE_END
首先获取var5就得预期值也可以叫做期待值
然后调用底层代码Unsafe_CompareAndSetInt。下面看Unsafe_CompareAndSetInt怎么做的
第一步通过 JNIHandles::resolve() 获取obj在内存中OOP实例
第二步根据成员变量value反射后计算出的内存偏移值offset去内存中取指针addr
最后通过Atomic::cmpxchg(x, addr, e)实现CAS
是不是很简单?
可能到这里对于内存中的偏移量什么还不清楚。所以咱们再看看offset怎么回事。
今天只是简单介绍内存,如果要详细写会涉及到缓存行,内存伪共享,CPU多级缓存。。。。
后面我写一点关于JVM的一点小理解的时候再详细说。
下面看我一个代码示例
/**
* @ClassName MemoryTest
* @Description ToDo
* @Author Allen
* @Date 2018/12/10 20:58
* @Version
*/
public class MemoryTest
{
private volatile int a ,b ,c ,d,e=3;
}
通过jol查看内存情况如下图
一个java对象可以看成是一段内存,各个字段都得按照一定的顺序放在这段内存里,同时考虑到对齐要求。
可能这些字段不是连续放置的,用这个方法能准确地告诉你某个字段相对于对象的起始内存地址的字节偏移量,因此是相对偏移量。
通过上面的解释明白了偏移量所以咱们取内存数据就是这么取到的。
欢迎扫码加入知识星球继续讨论