从CAS讲起,真正高性能解决并发编程的原子操作

 

今天是猿灯塔“365天原创计划”第1天。

一.原子性操作

原子性操作:原子性在一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。及时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。


 

 

上述代码中AtomicInteger.getAndIncrement()

就很好的帮我们在多线程业务场景中解决了原子性问题

深入源码底层发现它的方法源于:


 

继续深入:


 

发现实际上这个方法的实现原理就是CAS。


 

 

二.CAS

compare and swap 比较和交换

属于硬件同步原语属于CPU指令可以直接对内存做修改处理器提供了基本内存操作的原子性保证。看一下CAS原理图:


 

CAS需要有3个操作数:内存地址N,旧的预期值E,即将要更新的目标值N。

CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作。

首先读取开始变量值N,存入变量E,然后在进行++操作,计算后的结果值存入变量V  ,

然后在将E值写回,跟N值进行比较,如果此时E值与N值是一致,则这个操作直接结束,否则就要继续从开始部分重来一次,所以CAS也被成为自旋锁,或者说无锁。

JVM提供了一个工具类 Unsafe

Unsafe是CAS的核心类,由于java方法无法直接访问底层系统,需要通过本地(native)方法来访问,Unsafe相当于一个后门,基于该类可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中,其内部方法操作可以像C的指针一样直接操作内存,因为java中CAS操作的执行依赖于Unsafe类的方法

如下就是代码演示:


 

三.ABA问题

ABA问题的根本在于cas在修改变量的时候,无法记录变量的状态,比如修改的次数,是否修改过这个变量。这样就很容易在一个线程将A修改成B时,另一个线程又会把B修改成A,造成casd多次执行的问题。

解决方案:AtomicStampReference 

AtomicStampReference在cas的基础上增加了一个标记stamp,使用这个标记可以用来觉察数据是否发生变化,给数据带上了一种实效性的检验。它有以下几个参数:

//参数代表的含义分别是 期望值,写入的新值,期望标记,新标记值

publicbooleancompareAndSet(Vexpected,VnewReference,intexpectedStamp,intnewStamp);

public V getRerference();

public int getStamp();

publicvoidset(VnewReference,int newStamp);

 

代码我就不演示了~  希望这篇文章对各位有帮助吧!!!

  

posted @ 2020-03-26 16:45  猿灯塔  阅读(258)  评论(0编辑  收藏  举报