理解CAS
一、理解CAS是什么
之前学习的原子类的底层用的就是CAS,是compareAndSet的缩写,比较并交换的意思
二、使用方法
public class CasTest {
public static void main(String[] args) {
//创建integer的原子类,设置默认值是200
AtomicInteger atomicInteger = new AtomicInteger(200);
//比较并交换,如果是期望的值,那么就更新成新的数据。如果atomicInteger中的值是200,更新成201
atomicInteger.compareAndSet(200,201);
System.out.println(atomicInteger.get());
}
}
控制台打印结果:
三、CAS的缺点
1、底层调用自旋锁会循环耗时
2、一次性只能保证一个共享变量的原子性
3、ABA问题
理解unsafe类
一、学习Volatile认识和三大特性中,涉及到了一个原子类中的方法getAndIncrement(),相当与常规代码中的++自增,分析这个方法的底层,是用到了unsafe类
这里也用到了一个底层的CAS:比较当前工作内存中的值和主内存中的值,如果这个值是期望的,那么执行操作,如果不是就一直循环,因为这里用到了一个自旋锁
unsafe类:首先需要了解到Java是无法直接操作内存的,因此在和内存交互的时候就用到了native关键字,
调用本地方法库中的方法,调用C++来操作内存
而unsafe类是Java留的一个后门,我们也可以通过这个类来操作内存!
原子引用解决ABA问题
一、什么是ABA问题
A和B同时拿到了主存中相同的数据,B线程以很快的时间对数据进行了修改,并且又以很快的速度还原回了原来的数据,那么此时A线程却不知情主存中的数据发生了修改,所以A线程还是照旧执行他的任务,没有任何的反应
二、原子引用解决ABA问题
通俗的将就是带版本号的原子操作
涉及到了一个原子操作类,atomicStampedReference原子引用类(乐观锁机制),里面有获取版本号的方法
代码如下:
public class casDemo01 {
public static void main(String[] args) {
//创建带版本号的数据
AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(1,1);
new Thread(()->{
int stamp = atomicStampedReference.getStamp(); //获得版本号
System.out.println("A1-->" + stamp);
//模拟延时
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果期望的值是1,修改成2,更新版本号在原有基础上+1
System.out.println(atomicStampedReference.compareAndSet(1, 2, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("A2-->" + atomicStampedReference.getStamp());
//将线程A更新的数据修改回原来的数据
System.out.println(atomicStampedReference.compareAndSet(2, 1, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1));
System.out.println("A3-->" + atomicStampedReference.getStamp());
},"A").start();
new Thread(()->{
int stamp2 = atomicStampedReference.getStamp();
System.out.println("B1-->" + stamp2);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicStampedReference.compareAndSet(1, 6, stamp2, stamp2 + 1));
System.out.println("B1-->" + atomicStampedReference.getStamp());
},"B").start();
}
}