Java并发之CAS
2022-10-31 本文已影响0人
枫叶红花
一、什么是CAS
CAS(Compare And Swap,比较和交换),通常指的是这样一种原子操作:在修改某一个变量前,会先比较它内存中的值是否和期望的值一致,如果一致,就给它赋一个新值。CAS的比较判断、赋值操作,是一个不可分割的原子操作,并且这一操作是在硬件层面得到保障,在Intel处理器中,使用的是cmpxchg指令。
//v = 内存中的值,E = 期望值
if(v == E){
v = newValue;
}
二、Java中的CAS
Java中的CAS方法都是来自Unsafe类。
Java中提供了三种CAS方法:
// 第一个参数:对象的实例 第二个参数:内存偏移量 第三个参数:对比期望的值 第四个参数:字段的新值
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
//CAS示例
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(1);
//这个方法底层就是使用compareAndSwapInt()
boolean b = atomicInteger.compareAndSet(1, 3);
System.out.println("第一次修改的结果: "+b+ "修改后的值: " +atomicInteger.get());
boolean b1 = atomicInteger.compareAndSet(1, 6);
System.out.println("第二次修改的结果: "+b1+ "修改后的值: "+atomicInteger.get());
}
执行的结果:
执行结果
三、CAS缺陷
CAS的缺陷主要表现在以下三个方面:
1.在多线程竞争条件下,CAS操作失败,会进行自旋,如果竞争激烈,就会给CPU带来非常大的开销
2.CAS每次只能保证一个共享变量的原子操作
3.CAS会产生ABA问题
四、CAS的ABA问题
当多个线程对一个共享变量进行操作时,其中一个线程将变量的值从1改为2,但是立刻又将值从2改为1,那么对于其它线程而言是不可知的,仍然可以将变量的值修改成功。 ABA示意图public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(1);
new Thread(()->{
int value = atomicInteger.get();
log.debug("Thread1 read value:"+value);
LockSupport.parkNanos(100L);
//CAS操作将原值改为3
if(atomicInteger.compareAndSet(value,3)){
log.debug("Thread1 update from "+value+"to 3");
}else {
log.debug("Thread1 update fail!");
}
},"Thread1").start();
new Thread(()->{
int value = atomicInteger.get();
log.debug("Thread2 read value: "+value);
//CAS操作将原值改为2
if(atomicInteger.compareAndSet(value,2)){
log.debug("Thread2 update from "+value+"to 2");
value = atomicInteger.get();
log.debug("Thread2 read value:"+value);
//CAS操作将原值改为1
if(atomicInteger.compareAndSet(value,1)){
log.debug("Thread2 update from "+value+"to 1");
}
}
},"Thread2").start();
}
执行结果如下:
执行结果
五、ABA的解决方案
数据库中有个乐观锁,是基于版本控制实现数据同步的机制,当数据每修改一次,版本就会+1。
而Java中同样也提供了类似的工具类——AtomicStampedReference、AtomicMarkableReference。
如图所示,就是这个类使用时核心参数:
public static void main(String[] args) {
AtomicStampedReference atomicStampedReference = new AtomicStampedReference(1,1);
int stamp = atomicStampedReference.getStamp();
int reference = (int) atomicStampedReference.getReference();
boolean b2 = atomicStampedReference.compareAndSet(reference, 2, stamp, stamp + 1);
System.out.println("第一次操作的结果:"+b2+" reference的值:"+reference + " stamp的值:" +stamp);
reference = (int) atomicStampedReference.getReference();
boolean b3 = atomicStampedReference.compareAndSet(reference, reference + 1, stamp, stamp + 1);
System.out.println("第二次操作的结果:"+b3+" reference的值:"+reference + " stamp的值:" +stamp);
}
执行的结果:
执行结果