Java同時実行シリーズ-CAS詳細な説明

Java同時実行シリーズ-CAS詳細な説明

CAS

CASとは

CASは、CPUアトミック命令である比較交換を意味します。CASは、同期同期ロックとは異なる楽観的ロックを実装します(同期は悲観的ロックのアイデアであり、一種の相互排除ロックであり、取得したらリソースを取得します。私の手では、他のスレッドを解放するのを待ってください。CASは再試行メカニズムであり、失敗した場合は再試行してください)、複数のスレッドがCASを使用して変数を変更しようとすると、1つのスレッドのみが変数の値を更新できます、および他のスレッドはすべて失敗し、失敗したスレッドは中断されず、レースが失敗したことが通知され、再試行されます。比較と交換は2つのアクションですが、CASはハードウェアレベルでの比較と交換の原子性を保証します。実際、CASにもロック操作がありますが、このロックはCPUによって実現されます。バスロックの方法は、マルチプロセッシング間のアトミック操作を保証します。 。Synchroniseと比較すると、オブジェクトとモニターの効果ははるかに優れています。

ここでJavaメモリモデルについて考えてみてください。共有変数はメインメモリに格納され、各スレッドの作業メモリはコピーを保存します。CASで期待される値は、実際には変数のコピーです。スレッドがメインメモリから変数を取得すると、メインメモリの値が他のスレッドによって更新される場合があります。このとき、スレッドに保存されている値はメインメモリの値と一致せず、失敗します。失敗後、値はメインメモリから再読み込みされます。ループ操作。

使用例

AutomicXXXシリーズのクラスが最良の使用例です。AutomicInterがロックなしでデータセキュリティを確保する方法を見てみましょう。

   public static void main(String[] args) {
        AtomicInteger atomicInteger=new AtomicInteger(8);
        //当前值为8,期望值为8,改成10 成功  输出true
        System.out.println(atomicInteger.compareAndSet(8,10)+"目前的值为"+atomicInteger.get());
        //当前值为10,期望值为8 改成20 失败 输出false
        System.out.println(atomicInteger.compareAndSet(8,20)+"目前的值"+atomicInteger.get());
   }
复制代码

compareAndSetの最下層を見てみましょう

  public final boolean compareAndSet(int expect, int update) {
        //本地方法,调用CAS源语实现,CPU指令保证原子性,一直等待取到的值
        /**
        * 1:this代表的是unsafe这个类
        * 2:valueOffset:偏移量 valueoffset偏移量(从哪里来看下面的类说明),其实取到的就是目标值
        * 3:expect:期望的值
        * 4:update:更新的值
        *整句话实现的语意是如果this内的value(偏移量得到)和expect相等,就证明没有其他线程改变过这个变量,那么就更新它为update,				 
        * 没有成功的话没就采用自旋的方式继续进行CAS操作(感兴趣的小伙伴可以查看Hotspot源码具体试下过程,公众号程序员fly后面会出相			
        *文章,感兴趣的小伙伴可以关注下微信公众号)。
        **/
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

	/**
	*AtomicInteger类定义的变量和静态块
	*
	**/
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
        try {
           //valueOffset其实就是记录value的偏移量的
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

复制代码

CASの問題

CASはスピンロックであり、以下の問題があります

ABA問題

CAS在操作值的时候,会检查值有没有变化,比如原来有一个值是A,然后变成B,之后又变成了A,使用CAS技术进行检查的时候会发现值并没有发生变化,但实际其实是发生了变化的,这就是著名的CAS的ABA问题。常规的解决思路其实是加版本号每次变化的时候将版本号+1。整体变化过程就变成了1A-2B-3A。JDK在1.5之后的AtomicStampedReference就实现了这种实现来解决ABA问题。(AtomicMarkableReference也能解决ABA问题,它是通过一个boolean标记来标识是否有修改,不再是版本号,思想上其实都类似)

循环开销问题

自旋CAS如果长时间不成功的话,会给CPU带来很多开销。

保证一个共享变量原子操作

当对一个共享遍历操作的时候,我们可以循环使用CAS来保证原子操作,但是对多个共享遍历进行操作的时候,循环CAS无法保证原子性,但是有一个取巧的方法就是:多个共享变量合成一个变量进行操作,读过线程池源码同学的都知道,线程池中线程的数量以及线程的状态会合并成一个共享变量操作(线程池源码文章请关注公众号程序员fly了解相关详情)。

闲谈

感觉有帮助的同学还请点赞关注,这将对我是很大的鼓励~,公众号有自己开始总结的一系列文章,需要的小伙伴还请关注下个人公众号程序员fly呀,干货多多,湿货也不少(∩_∩)。

巨人肩膀

www.modb.pro/db/127003

juejin.cn/post/684490…

おすすめ

転載: juejin.im/post/7030002149362761758