Java并发编程之CAS二源码追根溯源java
在上一篇文章中,咱们知道了什么是CAS以及CAS的执行流程,在本篇文章中,咱们将跟着源码一步一步的查看CAS最底层实现原理。编程
本篇是《凯哥(凯哥Java:kagejava)并发编程学习》系列之《CAS系列》教程的第二篇:从源码追根溯源查看CAS最底层是怎么实现的。安全
本文主要内容:CAS追根溯源,完全找到CAS的根在哪里。并发
经过上一篇文章学习,咱们知道了AtomicInteger.compareAndSet方法不加锁能够保证原子性(其原理就是unsafe+cas实现的),咱们来看看其源码:ide
AtomicInteger对象(下文凯哥简称:atoInteger)怎么保证变量内存可见性呢?学习
查看源码:this
咱们来看看atoInteger的compareAndSet方法。凯哥在上面添加了注释。spa
在调用unsafe的compareAndSwapInt这个方法的时候,unsafe是什么?this指的是什么?valueOffset又是什么呢?操作系统
咱们接着查看atoInteger源码:线程
咱们发现Unsafe以及valueOffset都是从一个对象中获取到的。
那么this指的是什么?其实this就是当前atoInteger对象。
那么Unsafe对象在哪里呢?
咱们想要看源码,怎么查看呢?发现不能看源码啊。别急,这个文件的源码能够从openJdk的源码中查到。
接着,咱们来查看OpenJdk8的源码:
(PS:下载OpenJdk8源码凯哥这里就不赘述了。在文章最后,凯哥给出)
下载完,解压以后,文件位置:openjdk\jdk\src\share\classes\sun\misc。以下图:
咱们来看看Unsafe类上面的注解:
A collection of methods for performing low-level, unsafe operations.
什么意思呢?用于执行底层的(low-level,)、不安全操做的方法的集合。
就是说,这个类能够直接操做底层数据的。
须要说明的是:在这个对象中大量的方法使用了native来修饰(据网友统计高达82个)
咱们知道,Java的方法使用native关键字修饰的,说明这个方法不是Java自身的方法(非Java方法),可能调用的是其余语言的。如C或C++语言的方法。
咱们再来看看:unsafe.objectFieldOffse()中的
这个方法就是返回一个内存中访问偏移量。
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
在unsafe类中compareAndSwapInt方法也是native的。咱们在来看看这个方法调用操做系统底层C++的代码:
说明:
jint *addr:主内存中的变量值
old:对象工做区域的值
new_val:将要改变的值。
这三个是否是很熟悉,对。就是CAS的三个参数。
分析第13行为何返回false:
在11行的时候,设置主内存的变量值V=1.
在12行后,更新为V=2020了。
当执行到第13行的时候,
主内存:V=2020
程序工做区变量值jint *addr A的值:A=1
new_val:1024。
从调用C++代码咱们能够分析到:
在第5行的时候,由于1!=2020,因此return 的result就是false.
因此第13行输出的是false.
调用的是getAndAddInt方法。接着查看unsafe的源码,就会发现CAS保证原子性的终极代码。
看看:getObjectVolatile。方法发现是native.以下图:
再来看看compareAndSwapObject:
发现是native修饰的方法。说明不是Java的方法。这个咱们等会再细说。
源码:
咱们来模拟:atoInteger.getAndIncrement();
假设默认值是0. 主内存的值是0
在调用getAndSetObject方法的几个参数说明:
Var1:当前atoInteger对象
Var2:当前偏移量(内存地址所在位置。如:三排四列)
Vart4:默认就是1
Var5:获取到的主内存的值
Var5+var4:将要更新的值。
从源码,咱们看到是do while语句。为何不是while语句呢?由于先要获取到主内存中变量最新的值,而后再判断。因此选用了do while语句。
咱们来看看当CPU1线程1和CPU2线程B来执行的时候:
两个线程都从主内存copay了i的值到本身工做内存空间后,进行+1的操做。
假设线程1再执行+1操做后,准备往主内存回写数据的时候,CPU1被挂起。而后CPU2竞争到资源以后,也操做i+1后,将更新后的值回写到了主内存中。而后切换到CPU1了,CPU1接着执行。对比代码分析:
线程1在执行do后获得的值var5=1而不是0
而后while里面执行:var1和var2运算后的结果是0(工做区的值)。
由于0!=5 .因此this.comparAndSwapInt的值是false.
又由于前面有个! 非得符号。也就是!false。咱们知道!false就是true.
也就是while(true)。While(true)后,接着循环执行。线程会放弃原有操做,从新从主内存中获取到最新数据(此时就是1了),而后再进行操做后。
又到了do,获取在主内存最新数据是1.接着走while()
由于,var1,var2获取到工做区的值是1 var5也等于1.1=1,成立了,执行var5+var5=1+1=2,来更新主内存的数据后返回true.
又由于前面有个!非的符号。因此就是while(!true),也就是while(false)。退出循环,返回var5的值。
经过上面的运行分析,咱们发现atoInteger的getAndIncrement方法保证原子性是unsafe+CAS来保证变量原子性的(其中do while语句就是后面咱们将要学到的自旋)