CAS是什么
CAS是什么?
比较并交换
例子1:
public class ABADemo1 {
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5);
System.out.println(atomicInteger.compareAndSet(5,2019)+"\t当前值是:"+atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(5,1024)+"\t当前值是:"+atomicInteger.get());
}
} 结果如下:
CAS底层原理?
1.自旋锁
2.UnSafe类
atomicInteger.getAndIncrement();
底层调用的其实是unsafe方法,关系见下图:
va1 AtomicInteger对象本身。
var2 该对象的引用地址。
var4 需要变动的数量。
var5 是用var1 var2找出的主内存中真实的值,用该对象当前的值与var5比较,如果相同,更新var5+var4并且返回true,如果不同,继续取值然后再比较,直到更新完成。(自旋)
最后通过native调用本地方法,该方法的实现位于unsafe.cpp中。
1.Unsafe类:
是CAS的核心类,由于Java方法无法直接访问底层系统,需要通过本地方法(native)来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为java 中CAS的操作执行依赖于Unsafe类的方法。
注意Unsafe类中的所有方法都是native修饰的,也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务
2.变量valueOffset,表示该变量值在内存中的偏移地址,因为Unsafe就是根据内存偏移地址来获取数据的。
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
3.变量vaue用volatile修饰,保证了多线程之间的内存可见性。
CAS 的全称为Compare-And-Swap,它是一条CPU并发原语。
它的功能是判断内存某个位置的值是否为预期值,这个过程是原子的
CAS并发原语体现在Java语言就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法,JVM会帮我们实现CAS汇编指令。这是一种完全依赖于硬件的功能。通过它实现了原子操作。再次强调,由于CAS是一种系统原语,原语属于操作系统用于范畴,是由若干条指令组成的,用于完成某个功能的一个过程,并且原语的执行必须是连续的,再执行过程中不允许被中断,也就是说CAS是一条CPU的原子指令,不会造成所谓的数据不一致问题。
CAS 的缺点
1.循环时间长开销很大。
2.只能保证一个共享变量的原子操作。
3.会有ABA问题。
CAS会导致"ABA问题"
CAS算法实现一个重要前提需要取出内存中某时刻的数据并再当下时刻比较并替换,那么再这个时间差会导致数据的变化。
比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且线程two进行了一些操作将值变成了B,然后线程two又将V位置的数据变成A, 这时候线程one进行CAS操作发现内存中仍然是A,然后线程one操作成功。尽管线程one的CAS操作成功,但不代表这个操作过程就是没有问题的。
AtomicReference原子引用例子:
@Getter
@ToString
@AllArgsConstructor
class User {
String userName;
int age;
} /**
* AtomicReference原子引用,想对某个类进行包装,可以参考这个类的写法
*/
public class AtomicReferenceDemo { public static void main(String[] args) {
User z3 = new User("z3",22);
User li4 = new User("li4",25);
AtomicReference<User> atomicReference = new AtomicReference<>();
atomicReference.set(z3);
System.out.println(atomicReference.compareAndSet(z3,li4)+"\t"+atomicReference.get().toString());
System.out.println(atomicReference.compareAndSet(z3,li4)+"\t"+atomicReference.get().toString());
}
} 运行结果见下图:
解决ABA问题 加版本号例子: public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100,1);
public static void main(String[] args) {
System.out.println("-------------存在ABA问题");
new Thread(() ->{
atomicReference.compareAndSet(100,101);
atomicReference.compareAndSet(101,100);
},"T1").start(); new Thread(() ->{
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100,2019) +"\t" +atomicReference.get());
},"t2").start(); try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
} System.out.println("-------------以下是解决ABA问题"); new Thread(() ->{
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第1次版本号"+stamp);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100,101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第2次版本号"+atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101,100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
System.out.println(Thread.currentThread().getName()+"\t第3次版本号"+atomicStampedReference.getStamp());
},"t3").start(); new Thread(() ->{
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName()+"\t第1次版本号"+stamp);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean result = atomicStampedReference.compareAndSet(100,2019,stamp,stamp+1);
System.out.println(Thread.currentThread().getName()+"\t修改成功否"+result+"\t 当前版本号"+atomicStampedReference.getStamp()); System.out.println(Thread.currentThread().getName()+"\t当前实际最新值"+atomicStampedReference.getReference());
},"t4").start();
}
}
运行结果见下图:
最新文章
- WPF入门教程系列十五——WPF中的数据绑定(一)
- [Linux] CentOS 加入开机启动
- poj 2392 多重背包
- Python3基础 print 自带换行功能
- Sumblime Text2安装Package Control两种方法+安装插件+注册码
- js之内置对象
- [HDOJ5583]Kingdom of Black and White(暴力)
- JDBC与SQL SERVER各个版本的连接方法
- Java Interface and Abstraction
- Android开源项目发现---TextView,Button篇(持续更新)
- HDU_2028——求多个数的最小公倍数
- Cocos2dx中Plugin-X 在android下的整合
- 深入浅出数据结构C语言版(2)——简要讨论算法的时间复杂度
- JD . 简单的网站构成、引入图标、去除 图片间距/加粗/倾斜/下划线/蓝色外边框 禁止文本拖拽、文字居中、做logo、模拟鼠标 、不使用hover外部css样式实现hover鼠标悬停改变样式
- selenium2自动化测试学习笔记(二)
- ANI功能分析
- linux基本命令之stat
- EL和JSTL笔记
- 性感天才黑客乔治&#183;霍兹George Hotz 17岁打脸乔布斯20岁搞疯索尼
- 动态规划之139 Word Break
热门文章
- 21个项目玩转深度学习:基于TensorFlow的实践详解06—人脸检测和识别——项目集锦
- JavaScript数据类型总结
- javascript基础之数组一
- [POJ2528]Mayor&#39;s posters(离散化+线段树)
- 解决netcore在docker容器中连接oracle报错(timezone region not found)
- codeforces gym100801 Problem J. Journey to the “The World’s Start”
- asp.net core web api 发布到iis失败 错误500.19
- E420笔记本升级固态硬盘
- CachedRowSet 接口
- slim的简单使用