(ここから) 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の問題が発生すると、問題を自動的に修正するのではなく、成功しません。