学习方法:场景->需求->解决方案->应用->了解原理

一、CAS是什么?

CAS机制:CompareAndSwap 或 CompareAndExchange 或 CompareAndSet。
CAS是一个能够进行比较和替换的方法,这个方法能够在多线程环境下保证对一个共享变量进行修改时的原子性不变。

场景:i++ 保证原子性

为了更好的理解CAS机制,我们先看一个例子:

public class S01_AtomicDemo {volatile int i=0;//加上 synchronized 关键字保证结果一定是 2000 正确的public /*synchronized */ void incr(){i++;}public static void main(String[] args) throws InterruptedException {S01_AtomicDemo s01_atomicDemo = new S01_AtomicDemo();Thread[] threads = new Thread[2];for (int j=0; j <2 ; j++) {threads[j]=new Thread(()->{for (int k = 0; k < 1000; k++) {s01_atomicDemo.incr();}});threads[j].start();}threads[0].join();//保证线程执行结束threads[1].join();System.out.println(s01_atomicDemo.i);//期望结果是 两个线程分别执行 1000  i++,结果应该是 2000//但是实际结果小于2000,这是原子性问题——可能第一个线程已经加到i=100了,第二个线程读的还是i=0}
}

这个例子的结果:

  • 期望结果是 两个线程分别执行 1000 i++,结果应该是 2000
  • 在不加synchronized锁的同步锁的情况下,实际结果小于2000,这是原子性问题——可能第一个线程已经加到i=100了,第二个线程读的还是i=0

想到达结果正确,可以从两个方面考虑:

  • ① 不允许当前非原子指令在执行过程中被中断,也就是说保证i++操作在执行过程中不存在上下文切换。
  • ② 多线程并发执行导致原子性问题可以通过一个互斥条件来实现串行执行

synchronized锁可以实现。增加synchronized锁之后可以保证原子性(结果正确),但是加锁会存在性能问题。

需求:那除了加锁,还有没有更好的方式呢?

这个时候我们想到一种乐观锁的机制:在线程调用i++之前,先判断i的值和之前读取的 i 的预期值是否相等。如果相等,则说明 i 的值 没有被其他线程修改过,这个时候可以正常修改;否则,表示修改过,就要重新读取最新的 i 的值进行累加。

解决方案:CAS就是解决这个问题的。

应用:getAndIncrement()方法

public class S01_AtomicInteger {public static void main(String[] args) throws InterruptedException {AtomicInteger atomicInteger=new AtomicInteger(0);Thread[] threads=new Thread[2];for (int j = 0; j < 2 ; j++) {threads[j]=new Thread(()->{for (int i = 0; i < 1000; i++) {/*** getAndIncrement是原子累加方法,每次调用一次会在原来值的基础上+1,这个过程采用了CAS机制保证原子性。点进去看源码*/atomicInteger.getAndIncrement();}});threads[j].start();}threads[0].join(); //保证线程执行结束threads[1].join();System.out.println(atomicInteger); //结果是2000,没问题!}
}

二、CAS原理示意图

该图表示通过CAS对变量V进行原子更新操作。CAS方法中会传递三个参数,第一个参数V表示要更新的变量,第二个参数E表示期望值,第三个参数U表示更新后的值。更新的方式是,如果V==E,表示预期值和实际值相等,则将V修改成U并返回true,否则修改失败返回false。
在Java中的Unsafe类中提供了CAS方法,针对int类型变量的CAS方法定义如下。


从方法定义中可以看到,它有四个参数:

  • o,表示当前的实例对象。
  • offset,表示实例变量的内存地址偏移量(内存中实际值)。
  • expect,表示预期值(更新前的值)。
  • update,表示要更新的值(更新后的值)。

expect和update比较好理解,offset表示目标变量X在实例对象o中内存地址的偏移量。简单来说,在预期值expect要和目标变量X进行比较是否相等的判断中,目标变量X的值就是通过该偏移量从内存中获得的。

三、CAS在AtomicInteger中的应用以及源码分析

z通过CAS在AtomicInteger中的应用来理解CAS机制和源码:

public class S01_AtomicInteger {public AtomicInteger atomicInteger=new AtomicInteger(0);public void add(){/*** getAndIncrement是原子累加方法,每次调用一次会在原来值的基础上+1,这个过程采用了CAS机制保证原子性。点进去看源码*/atomicInteger.getAndIncrement();}}

其中,valueOffset表示AtomicInteger中的成员变量value在内存中的偏移量,后续会用它直接从内存中读取value属性当前的值。
valueOffset的初始化方法如下( 点击valueOffset): 

// setup to use Unsafe.compareAndSwapInt for updates
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;

valueOffset用到了unsafe.objectFieldOffset()方法,获取value字段在AtomicInteger.class中的偏移量。

个人理解: objectFieldOffset(AtomicInteger.class.getDeclaredField("value"))方法,使用反射(getDeclaredField)来获取value字段在AtomicInteger.class中的属性值。

结合这段代码的分析,对前面提到的o和offset这两个字段的含义就不难理解了,o 表示当前的实例对象,offset 表示要更新的变量(个人理解:在AtomicInteger中是指value字段)。
在CAS中,我们需要通过expect去和某个字段的值进行比较,而expect比较的目标值就是通过offset找到某个字段在内存中的实际值(在AtomicInteger中是指value字段),如果相等,就修改成update并返回true,否则返回false。
unsafe.getAndAddInt(this,valueOffset,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;
}

代码整理(形参名):

public final int getAndAddInt(Object o,long offset,int n){int v;do {v=this.getIntVolatile(o,offset);}while (!(this.compareAndSwapInt(o,offset,v,v+n)));return v;
}

代码实现逻辑分析如下:
●“v = this.getIntVolatile(o, offset); ”表示根据value在对象o的偏移量来获得当前的值v。
●使用compareAndSwapInt()方法实现比较和替换,如果value当前的值和v相等,说明数据没有被其他线程修改过,则把value修改成v+n。
●这里采用了循环来实现,原因想必大家能猜测到。如果compareAndSwapInt()方法执行失败,则说明存在线程竞争,但是当前的方法是进行原子累加,所以必须要保证成功,为了达到这个目的,就只能不断地循环重试,直到修改成功后返回。
●整体来说,CAS就是一种基于乐观锁机制来保证多线程环境下共享变量修改的原子性的解决方案。前面分析的案例虽然是在Java中的应用场景,但是它本质上和synchronized同步锁中用到的CAS是相同的,我们来看一下Unsafe类中CAS的定义。

public final native boolean compareAndSwapInt(Object o, long offset, int expect, int update);

compareAndSwapInt()是一个native方法,该方法是在JVM中定义和实现的。

四、总结:i++对应CAS示意图进行详解:

在Java中的Unsafe类中提供了CAS方法,针对int类型变量的CAS方法定义如下。



从方法定义中可以看到,它有四个参数: ● o,表示当前的实例对象。 ● offset,表示实例变量的内存地址偏移量(内存中实际值)。 ● expect,表示预期值(更新前的值)。 ●update,表示要更新的值(更新后的值)。 expect和update比较好理解,offset表示目标变量X在实例对象o中内存地址的偏移量。简单来说,在预期值expect要和目标变量X进行比较是否相等的判断中,目标变量X的值就是通过该偏移量从内存中获得的。

offset是要修改的value字段(内存中实际存的值),expect期望值是value字段从内存中取出来时存在的值(对i++操作来说,即是i进行++之前的值),如果相等,则修改为v+n。

CAS机制实现原理分析相关推荐

  1. Redis数据持久化机制AOF原理分析一---转

    http://blog.csdn.net/acceptedxukai/article/details/18136903 http://blog.csdn.net/acceptedxukai/artic ...

  2. Java 数据交换格式反射机制SpringIOC原理分析

    数据交换格式&反射机制&SpringIOC原理分析 什么是数据交换格式? 数据交换格式使用场景 JSON简单使用 什么是JSON? JSON格式的分类 常用JSON解析框架 使用fas ...

  3. Redis数据持久化机制AOF原理分析二

    本文所引用的源码全部来自Redis2.8.2版本. Redis AOF数据持久化机制的实现相关代码是redis.c, redis.h, aof.c, bio.c, rio.c, config.c 在阅 ...

  4. 事件争夺战 Android事件处理机制与原理分析

    事件争夺战 Android事件处理机制与原理分析 文章目录 事件争夺战 Android事件处理机制与原理分析 View的继承关系 View的事件处理源码 总结: ViewGroup的事件分发源码 总结 ...

  5. Android--Handler使用应运及消息机制处理原理分析

    最近开通了一个小微博,欢迎大家关注,每天分享一些上班路上看的小知识点 点击打开链接 一.Handler是什么 ? handler是android给我们提供的一套用来更新UI的一套机制,也是一套消息处理 ...

  6. 编译原理实验语义分析_Windows MVSC编译器实现Xtended Flow Guard(XFG)保护机制的原理分析...

    一.前言 近期,微软正在开发Xtended Flow Guard(XFG),这是Control Flow Guard(控制流防护,CFG)的演进版本,作为其自身的控制流完整性实现.XFG通过不同类型函 ...

  7. Windows MVSC编译器实现Xtended Flow Guard(XFG)保护机制的原理分析

    一.前言 近期,微软正在开发Xtended Flow Guard(XFG),这是Control Flow Guard(控制流防护,CFG)的演进版本,作为其自身的控制流完整性实现.XFG通过不同类型函 ...

  8. shuffle机制和原理分析

    Shuffle简介 Shuffle描述着数据从map task输出到reduce task输入的这段过程.shuffle是连接Map和Reduce之间的桥梁,Map的输出要用到Reduce中必须经过s ...

  9. spark基础之shuffle机制和原理分析

    一 概述 Shuffle就是对数据进行重组,由于分布式计算的特性和要求,在实现细节上更加繁琐和复杂 在MapReduce框架,Shuffle是连接Map和Reduce之间的桥梁,Map阶段通过shuf ...

最新文章

  1. python3 open函数_python中open函数的基本用法示例
  2. JVM(4)之 使用MAT排查堆溢出
  3. 编译原理(三)之语义分析
  4. 如何:让Oracle表及字段显示为区分大小写
  5. Cocos2d 利用继承Draw方法制作可显示三维数据(宠物三维等)的三角形显示面板...
  6. IC卡CPU卡32位单片机S3系列接触式读写模块分类与性能攻略
  7. unity粒子特效与ui遮盖显示
  8. 面向接口编程(面向协议编程)
  9. 一篇文章带你了解国企程序员(超详细)
  10. php中根据数字月份返回月份的英文缩写
  11. WinCE 编程实验(第一章 引言)
  12. 利率浮动幅度bp什么意思,浮动利率bps换算百分比
  13. Cura参数设置-避免支撑拆除带来的困难
  14. 一个最低限度的国学书目
  15. HTML5 postMessage 和 onmessage API 详细应用
  16. c语言圆环杀人的题目,圆的周长的练习题[1]
  17. Linux内核 LCD 驱动程序框架
  18. 读书笔记与思考(一)《MIT深度思考法》
  19. 直流双闭环pwm调速系统matlab仿真,双闭环可逆直流脉宽调速系统的设计和仿真
  20. 浅谈游戏《超级马里奥:奥德赛》

热门文章

  1. Cloud ❀ Docker的基础操作命令
  2. C语言解决 加油站问题
  3. PyTorch seq2seq translation 使用序列到序列的网络和注意机制进行翻译
  4. 商业元年,代理刷脸支付平台选择很重要
  5. 计算机系军训口号四句霸气,霸气的军训口号大全
  6. 第八届蓝桥杯B组省赛题
  7. 华东理工计算机系专业考研难度,华东理工大学考研难度怎么样
  8. 6月09日国内四大证券报刊头版内容精华摘要
  9. 小米8 安装apk提示签名不一致
  10. 生日祝福小程序_热点丨小团团生日会享受史诗级牌面:DY官博更改头像送祝福!万达、西湖地贴屏显画面满满!...