CASのABA問題の解決策

ここから)    CAS問題の生成から

 

ロックフリー操作にCASを使用すると、古典的なABAの問題があります。

スレッド1はCASを使用して変数の値をAからBに置き換える準備ができています。その前に、スレッド2は変数の値をAからCに、次にCからAに置き換えます。その後、スレッド1は変数の値がA、CASは成功しています。しかし、実際には、この時点でのシーンはオリジナルとは異なります。CASは成功しますが、次の例のような隠れた問題がある可能性があります。

単一リンクリストで実装されたスタックがあります。スタックの最上部はAです。この時点で、スレッドT1はすでにA.nextがBであることを認識しており、CASを使用してスタックの最上部をBに置き換えます。

head.compareAndSet(A、B);

T1が上記の命令を実行する前に、スレッドT2が介入し、AとBをスタックからプッシュし、次にpushD、C、Aをプッシュします。このとき、スタック構造は次のようになり、このときオブジェクトBは解放されています。

この時点で、CAS操作を実行するのはスレッドT1のターンであり、検出によりスタックのトップはまだAであることが検出されたため、CASは成功し、スタックのトップはBになりますが、実際にはB.nextはnullなので、このときの状況は次のようになります。

その中で、スタックには要素が1つしかなく、CとDで構成されるリンクリストはスタックに存在せず、CとDは理由もなく破棄されます。

上記は、ABA問題によって引き起こされる隠れた危険です。さまざまな楽観的ロックの実装では、通常、バージョンスタンプを使用してレコードまたはオブジェクトにマークを付け、並行操作によって引き起こされる問題を回避します。Javaでは、AtomicStampedReference <E>もこれを実装します関数は、ABAの問題を回避するために[E、Integer]のタプルをラップすることでオブジェクトのバージョンにスタンプを付けます。たとえば、次のコードはAtomicIntegerとAtomicStampedReferenceを使用して、アトミック整数変数を初期値100、AtomicIntegerで更新しますCAS操作は正常に実行され、AttomicStampedReferenceとスタンプされたバージョンは、ABA問題のCASの実行に失敗します。

public  class Test {
     private  static AtomicInteger atomicInt = new AtomicInteger(100 );
    プライベート 静的 AtomicStampedReference atomicStampedRef = new AtomicStampedReference(100、0 ); 

    パブリック 静的 ボイド(文字列[]引数)主は、スローInterruptedExceptionある{ 
       INTT1スレッド = 新しいスレッド(新しいRunnableを(){ 
           @Override 
           公共 ボイドラン(){ 
              atomicInt.compareAndSet( 100、101)。
              atomicInt.compareAndSet( 101、100 )。
           } 
       }); 

       スレッドINTT2 = 新しいスレッド(新しいRunnableを(){ 
           @Override 
           公共 ボイドラン(){
               試み{ 
                  TimeUnit.SECONDS.sleep( 1 ); 
              } キャッチ(InterruptedExceptionある電子){ 
              } 
              ブール C3 = atomicInt.compareAndSet(100、101 )。
              System.out.println(c3); // true 
           } 
       }); 

       intT1.start();
       intT2.start(); 
       intT1.join(); 
       intT2.join(); 

       スレッドrefT1 = 新しいスレッド(新しいRunnableを(){ 
           @Override 
           公共 ボイドラン()
               のtry { 
                  TimeUnit.SECONDS.sleep( 1 ); 
              } キャッチ(InterruptedExceptionある電子){ 
              } 
              atomicStampedRef.compareAndSet( 100、101、atomicStampedRef.getStamp() 、atomicStampedRef.getStamp()+ 1 ); 
              atomicStampedRef.compareAndSet( 101、100、atomicStampedRef.getStamp()、atomicStampedRef.getStamp()+ 1 ); 
           } 
       }); 

       refT2スレッド = 新しいスレッド(新しいRunnableを(){ 
           @Override 
           公共 のボイドの実行(){
               int型のスタンプ= ; atomicStampedRef.getStamp()
               のtry { 
                  TimeUnit.SECONDS.sleep( 2 ); 
              } キャッチ(InterruptedExceptionある電子){ 
              } 
              ブール C3 =をatomicStampedRef.compareAndSet(100、101、stamp、stamp + 1 ); 
              System.out.println(c3);// false 
           }  
       });

       refT1.start(); 
       refT2.start(); 
    }

PS:AtomicStampedReferenceはCAS ABAの問題を解決します。実際には、ABAの問題が発生すると、問題を自動的に修正するのではなく、成功しません。

おすすめ

転載: www.cnblogs.com/max-home/p/12720374.html