CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
import java.lang.reflect.Field;
import sun.misc.Unsafe;
public class UnsafeTest{
private static Unsafe unsafe;
static {
try {
//通过反射获取rt.jar下的Unsafe类
Fieldfield = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe =(Unsafe) field.get(null);
} catch (Exceptione) {
System.out.println("GetUnsafe instance occur error"+e);
}
}
public static void main(String[]args) throws Exception
{
Class clazz= Target.class;
Field[]fields = clazz.getDeclaredFields();
System.out.println("fieldName:fieldOffset");
for (Fieldf : fields) {
// 获取属性偏移量,可以通过这个偏移量给属性设置,偏移量固定不变的
System.out.println(f.getName()+ ":" + unsafe.objectFieldOffset(f));
}
Targettarget = new Target();
FieldintFiled = clazz.getDeclaredField("intParam") ;
int a=(Integer)intFiled.get(target) ;
System.out.println("原始值是:"+a); //当前线程执行到该地方获取到最新值
/*intParam的字段偏移是12 原始值是3 我们要改为10
将上步获取值和通过字段偏移量获取该属性在该对象中位置(该字段一般设置volatile原语保证该线程立即可见最新值),然后判断比较并赋值
如果相同则赋值,否则不同就是被其它线程已更改,那么跳过不更新
*/
System.out.println(unsafe.compareAndSwapInt(target,12, 3, 10));
int b=(Integer)intFiled.get(target);
System.out.println("改变之后的值是:"+b);
//这个时候已经改为10了,所以会返回false
System.out.println(unsafe.compareAndSwapInt(target,12, 3, 10));
FieldstrParam = clazz.getDeclaredField("strParam") ;
Stringstra=(String)strParam.get(target) ;
System.out.println("改变之前的值是:"+stra);
System.out.println(unsafe.compareAndSwapObject(target,24, null, "5"));
Stringstrb=(String)strParam.get(target) ;
System.out.println("改变之后的值是:"+strb);
/*
fieldName:fieldOffset
intParam:12
longParam:16
strParam:24
strParam2:28
原始值是:3
true
改变之后的值是:10
false
改变之前的值是:null
true
改变之后的值是:5
**/
}
}
class Target{
volatile int intParam=3;
volatile long longParam;
volatile String strParam;
volatile String strParam2;
}
unsafe.compareAndSwapInt(this,valueOffset, expect, update);
类似:
if(this == expect) {
this= update
returntrue;
}else {
returnfalse;
}
那么问题就来了,成功过程中需要2个步骤:比较this== expect,替换this= update,compareAndSwapInt如何这两个步骤的原子性呢? 参考CAS的原理。
CAS是一个CPU指令所以是原子操作native方法
ABA问题。因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化了。ABA问题的解决思路就是使用版本号。在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A