CAS 是AtomicInteger类中compareAndSet()方法的缩写
import java.util.concurrent.atomic.AtomicInteger;
/**
* CAS 是compareAndSet的缩写 译:比较并交换 此操作是在内存中进行
*/
public class CASTest {
public static void main(String[] args) {
//设定初始值1
AtomicInteger atomicInteger = new AtomicInteger(1);
/**
* expect 预期值 update更新值
* 如果当前值 ==为预期值,则将该值原子设置为给定的更新值。 否则不更新
*/
atomicInteger.compareAndSet(1,2);
System.out.println(atomicInteger.get());
atomicInteger.compareAndSet(2,6);
System.out.println(atomicInteger.get());
}
}
compareAndSet()方法是java用来操作计算机内存的 ,为什么这么说呢,看一下源码
AtomicInteger类
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
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;
因为java无法直接操作计算机内存,这里创建了一个Unsafe对象,Unsafe对象里都是native方法用来操作C++,而C++可以操作内存
valueOffset = unsafe.objectFieldOffset //获取获取内存地址的偏移值
private volatile int value; 具体的值value是由volatile 来保证里面的值避免指令重排
源码举例:
//调用getAndIncrement()方法 来执行 ++ 操作
atomicInteger.getAndIncrement();
方法内部三个参数,分别是this(当前对象)valueOffset内存地址, 值1
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
对应this = var1,valueOffset = var2, var4 = 1;
这里进行了 do while 自旋锁
把当前对象地址的值 放到了var5中
如果var1 var2 中的值是 当前var5的值 那么就进行 当前值var5 +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;
}
总结 :
CAS:比较当前工作内存张红的值和主内存的值,如果这个值是期望的,那么则执行操作,如果不是就一直循环
在synchronized阻塞算法中,性能上有了很大的提升,高效的解决了原子操作
缺点:
1.循环会耗时,开销大
2.一次只能保证一个共享变量的原子性
3.ABA问题
什么是ABA问题 (类似狸猫换太子的故事)
public class CASTest {
public static void main(String[] args) {
//设定初始值1
AtomicInteger atomicInteger = new AtomicInteger(1);
//假设线程A 把1改成了2 然后又改成了1
atomicInteger.compareAndSet(1,2);
System.out.println(atomicInteger.get());
atomicInteger.compareAndSet(2,1);
System.out.println(atomicInteger.get());
//线程B根本不知道这个1 已经不是原来的值了 ,已经被动过手脚了
atomicInteger.compareAndSet(1,6);
System.out.println(atomicInteger.get());
}
}
如何解决呢
使用带版本好的原子类AtomicStampedReference
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicStampedReference;
/**
* CAS 是compareAndSet的缩写 译:比较并交换 此操作是在内存中进行
*/
public class CASTest {
public static void main(String[] args) {
//设定初始参考值1 initialStamp初始印记1 这里的initialStamp就是一个标记, 类似于乐观锁中的版本号
AtomicStampedReference<Integer> asr = new AtomicStampedReference<>(1, 1);
new Thread(() -> {
int stamp = asr.getStamp();
System.out.println(Thread.currentThread().getName() + "A1" + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
/**
* expectedReference 预期值
* newReference 新值
* expectedStamp 当前标记
* newStamp 新标记
*/
asr.compareAndSet(1, 2, asr.getStamp(), asr.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "A2" + asr.getStamp());
asr.compareAndSet(2, 1, asr.getStamp(), asr.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "A3" + asr.getStamp());
}, "A").start();
new Thread(() -> {
int stamp = asr.getStamp();
System.out.println(Thread.currentThread().getName() + "B1" + asr.getStamp());
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
asr.compareAndSet(1, 6, asr.getStamp(), asr.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "B2" + asr.getStamp());
}, "B").start();
}
}
这里遇到一个坑
Integer的值过大 ,会造成程序的预期结果不一样
Integer使用了对象缓存机制, 默认范围是-128~-127, 推荐使用静态工厂方法valueOF获取对象实例, 而不是new 因为valueOf使用了缓存,而new一定会创建新的对象分配新的内存空间