CAS(compareAndSet:比较并交换)
CAS是cpu并发原语,理解步骤如下:
- 使用java.util.concurrent.atomic包下的原子类
- 原子类的底层是使用unsafe(java的后门,通过这个类来操作内存)并计算出内存偏移量,value使用volatile注释
- 用getAndIncrement()为例,他传的参为: 当前对象,和内存偏移量,和数字1
- 从下图可以看出,他根据当前对象和内存偏移量计算出当真实内存位置,然后将该位置加1(自旋锁)
cas计算方式
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//如果达到期望值,就更新,否则不更新
boolean b = atomicInteger.compareAndSet(2020, 2021);
System.out.println(atomicInteger.get());
}
总结:
- 比较当前工作内存中的值和主内存中的值,如果这个值是期望的那么则执行操作!如果不是就一直循环
- java无法操作内存
- java可以调用c++ ( native)
- c++可以操作内存
- unsafe:java的后门,通过这个类来操作内存
缺点
- 循环会耗时
- 一次性只能保证一个共享变量的原子性
- ABA问题
CAS:ABA问题(狸猫换太子):
多个线程操作同一个变量,其中一个线程操作过这个变量,另一个线程并不知情。
代码如:
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(2020);
//如果达到期望值,就更新,否则不更新
boolean b = atomicInteger.compareAndSet(2020, 2021);
System.out.println(atomicInteger.get());
boolean c = atomicInteger.compareAndSet(2021, 2020);
System.out.println(atomicInteger.get());
boolean d = atomicInteger.compareAndSet(2020, 2222);
System.out.println(atomicInteger.get());
}
解决ABA问题:原子引用(使用更改后会有版本号)
代码:
//注意使用integer不能使用-128~127之外的数
public static void main(String[] args) {
// AtomicInteger atomicInteger = new AtomicInteger(2020);
AtomicStampedReference<Integer> atomicInteger = new AtomicStampedReference<>(1,1);
new Thread(()->{
int stamp = atomicInteger.getStamp();//获得版本号
System.out.println("a1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger.compareAndSet(1, 2,
atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a2=>"+atomicInteger.getStamp());
System.out.println(atomicInteger.compareAndSet(2, 1,
atomicInteger.getStamp(), atomicInteger.getStamp() + 1));
System.out.println("a3=>"+atomicInteger.getStamp());
},"a").start();
new Thread(()->{
int stamp = atomicInteger.getStamp();//获得版本号
System.out.println("b1=>"+stamp);
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicInteger.compareAndSet(1, 6,
stamp, stamp + 1));
System.out.println("b2=>"+atomicInteger.getStamp());
},"b").start();
}