jdk与mysql的cas乐观锁

全部过程有两个关键点:violate和本地原子操作

 

语录:

 

从内存领域来说这是乐观锁,因为它在对共享变量更新之前会先比较当前值是否与更新前的值一致,如果是,则更新,如果不是,则无限循环执行(称为自旋),直到当前值与更新前的值一致为止,才执行更新。

而Synchronized是一种悲观锁,它认为在它修改之前,一定会有其它线程去修改它,悲观锁效率很低。

点击打开链接

 

 

下面就分析一下:

先说violate,

 

AtomicInteger属性:

 

 

[java] view plain copy
 
  1. private volatile int value;  
  2.   
  3. /** 
  4.  * Creates a new AtomicInteger with the given initial value. 
  5.  * 
  6.  * @param initialValue the initial value 
  7.  */  
  8. public AtomicInteger(int initialValue) {  
  9.     value = initialValue;  
  10. }  
  11.   
  12. /** 
  13.  * Creates a new AtomicInteger with initial value {@code 0}. 
  14.  */  
  15. public AtomicInteger() {  
  16. }  
 


value是一个volatile变量,在内存中可见,任何线程都不允许对其进行拷贝,因此JVM可以保证任何时刻任何线程总能拿到该变量的最新值

 

 

 

 

看看getAndIncrement()方法是怎么利用CAS实现的。

 

[java] view plain copy
 
  1. /** 
  2.     * Atomically increments by one the current value. 
  3.     * 
  4.     * @return the previous value 
  5.     */  
  6.    public final int getAndIncrement() {  
  7.        return unsafe.getAndAddInt(this, valueOffset, 1);  
  8.    }  

 

此处调用unsafe 包的方法,将AtomicIntger对象和value的偏移量,用于底层硬件级别cas的取得value值,其功能相当于引用传递(如c语言实现)

 

[java] view plain copy
 
  1. public final int getAndAddInt(Object o, long offset, int delta) {  
  2.         int v;  
  3.         do {  
  4.             v = getIntVolatile(o, offset);//------------0---------------  
  5.         } while (!compareAndSwapInt(o, offset, v, v + delta));//-------------1-------------  
  6.         return v;  
  7.     }  

此处首先取得value值  v,此值作为期望值,交给本地方法compareAndSwapInt,

[java] view plain copy
 
  1. /** 
  2.    * Atomically update Java variable to <tt>x</tt> if it is currently 
  3.    * holding <tt>expected</tt>. 
  4.    * @return <tt>true</tt> if successful 
  5.    */  
  6.   public final native boolean compareAndSwapInt(Object o, long offset,//---------------2--------------  
  7.                                                 int expected,  
  8.                                                 int x);  


我稍微解释一下,其实compareAndSwapInt的注释解释的很明确,原子的将变量的值更新为x,如果成功了返回true,我们知道,如果我们创建AtomicInteger实例时不传入参数,则原始变量的值即为0,所以上面//----------0-----------处得到的v的值即为0,1处的代码为:

 

while(!compareAndSwapInt(o, offset, 0, 1))我们知道offset指向的地址对应的值就是原始变量的初值0,所以与期望的值0相同,所以将初值赋值为1,返回true,取反后为false,循环结束,返回v即更新之前的值0. 这就是类似于i++操作的原子操作的实现,当然最终CAS的实现都是native的,用C语言实现的,我们这里看不到源码,有时间我会反编译一下这段代码看看。

 

CAS线程安全

说了半天,我们要回归到最原始的问题了:这样怎么实现线程安全呢?请大家自己先考虑一下这个问题,其实我们在语言层面是没有做任何同步的操作的,大家也可以看到源码没有任何锁加在上面,可它为什么是线程安全的呢?这就是Atomic包下这些类的奥秘:语言层面不做处理,我们将其交给硬件—CPU和内存,利用CPU的多处理能力,实现硬件层面的阻塞,再加上volatile变量的特性即可实现基于原子操作的线程安全。所以说,CAS并不是无阻塞,只是阻塞并非在语言、线程方面,而是在硬件层面,所以无疑这样的操作会更快更高效!

 

主要引用:Java之美[从菜鸟到高手演练]之atomic包的原理及分析

 

 

 

 

 

 

其c语言实现为:

 

int compare_and_swap (int* reg, int oldval,int newval)
{
  ATOMIC();
  int old_reg_val = *reg;
  if (old_reg_val == oldval)
     *reg = newval;
  END_ATOMIC();
  return old_reg_val;
}

 

 

主要流程总结:

getvolatile                                              jdk  volatile关键字                   mysql rc隔离级别

do something

while {原子的compare and set}            jdk c++本地代码                    mysql db的原子性

 

get volatile jdk中表现为volatile关键字,即其它线程对共享变量的更新立即对本线程可见,mysql中表现为rc隔离级别,即其它事务的提交对本事务多次read操作可见,而不限于

posted on 2019-07-12 10:55  silyvin  阅读(634)  评论(0编辑  收藏  举报