Java での cas 操作の分析

黄舟
リリース: 2017-09-15 10:52:39
オリジナル
1543 人が閲覧しました

この記事では、Java プログラミングにおける cas 操作の概念、原則、使用法を例を通して分析します。これは一定の参考価値があり、必要な友人はそれについて学ぶことができます。

CAS は、メモリ内の共有データを操作する、最新の CPU で広くサポートされている特別な命令を指します。この命令は、メモリ内の共有データに対してアトミックな読み取りおよび書き込み操作を実行します。

この命令の動作プロセスを簡単に紹介します: まず、CPU はメモリ内で変更されるデータと期待値を比較します。次に、2 つの値が等しい場合、CPU はメモリ内の値を新しい値に置き換えます。それ以外の場合、操作は実行されません。最後に、CPU は古い値を返します。この一連の操作はアトミックです。複雑に見えますが、これらが Java 5 の同時実行メカニズムが元のロック メカニズムよりも優れている根本的な理由です。簡単に言えば、CAS の意味は、「元の値はどうあるべきだと思いますか。そうであれば、元の値を新しい値に更新してください。そうでない場合は変更せず、元の値が何であるかを教えてください。」ということです。 (この説明は『Java並行プログラミング実践編』より引用しています)

簡単に言うと、CASにはメモリ値V、古い期待値A、変更される新しい値Bの3つのオペランドがあります。期待値 A とメモリ値 V が同じである場合にのみ、メモリ値 V を B に変更し、それ以外の場合は V を返します。これは、変更される前に他のスレッドが変更しないと考える楽観的なロックの考え方であり、変更される前に他のスレッドが変更するだろうと考えるのが悲観的なロックです。

以下の簡単な例を見てください:


if(a==b) { 
  a++; 
}
ログイン後にコピー

a++ を実行する前に a の値が変更されたらどうなるかを想像してみてください。 a++ はまだ実行中ですか?この問題の原因は、マルチスレッド環境では a の値が不確実な状態にあるためです。このような問題はロックによって解決できますが、CAS はロックなしでも解決できます。


int expect = a; 
if(a.compareAndSet(expect,a+1)) { 
  doSomeThing1(); 
} else { 
  doSomeThing2(); 
}
ログイン後にコピー

こうすることで、aの値が変更されてもa++は実行されなくなります。

上記の書き方では、a!=expectの後、a++は実行されませんが、whileループ


while(true) { 
  int expect = a; 
  if (a.compareAndSet(expect, a + 1)) { 
    doSomeThing1(); 
    return; 
  } else { 
    doSomeThing2(); 
  } 
}
ログイン後にコピー

を使用すれば問題ありません。上記の記述方法は、A++ 操作がない場合、ロックなしで実装されます。これは実際にはノンブロッキング アルゴリズムです。

Application

java.util.concurrent.atomic パッケージのほとんどのクラスは CAS 操作を使用し、その主要なメソッドのいくつかの実装を確認する例として AtomicInteger を取り上げます。 JDK ドキュメントの getAndSet メソッドの説明は次のとおりです。指定された値にアトミックに設定し、古い値を返します。アトミック メソッドが反映されるところは、compareAndSet に反映されます。compareAndSet がどのように実装されるかを見てみましょう:


public final int getAndSet(int newValue) { 
  for (;;) { 
    int current = get(); 
    if (compareAndSet(current, newValue)) 
      return current; 
  } 
}
ログイン後にコピー

予想どおり、Unsafe クラスの CAS 操作を使用して完了します。

a++ 操作がどのように実装されるかを見てみましょう:


public final boolean compareAndSet(int expect, int update) { 
  return unsafe.compareAndSwapInt(this, valueOffset, expect, update); 
}
ログイン後にコピー

は元の例とほぼ同じで、自動インクリメント操作を実装するために CAS 操作も使用します。

++a 操作は、返される結果が異なることを除いて、a++ 操作と似ています


public final int getAndIncrement() { 
  for (;;) { 
    int current = get(); 
    int next = current + 1; 
    if (compareAndSet(current, next)) 
      return current; 
  } 
}
ログイン後にコピー
さらに、java.util.concurrent.ConcurrentLinkedQueue クラスはノンブロッキング アルゴリズムを使用し、ロックは使用されませんCASの運用に基づいて実装されています。 CAS の動作は JAVA 同時実行フレームワークの基礎と言え、フレームワーク全体の設計は CAS の動作に基づいて行われます。

短所:

1. ABAの問題

ウィキペディアが生きた例を挙げています -

あなたがお金でいっぱいのスーツケースを持って空港にいると、セクシーな男がやって来ます、そして彼女はあなたをとてもからかいましたあなたが注意を払っていないときに、彼女は同じスーツケースとお金がいっぱい入ったあなたのスーツケースを交換し、そして去っていきました。あなたのスーツケースがまだそこにあることに気づき、あなたはそれを持って飛行機に乗りました。

これがABAの問題です。

CAS 操作は、ABA の問題を引き起こしやすいです。つまり、a++ を実行する間に a が複数のスレッドによって変更された可能性がありますが、この時点では、CAS は a の値が元の値に戻っただけであると認識します。変わっていない。 a しばらく外を歩き回った後は、何も悪いことはしていないと断言できます。 !たぶん、楽しみのために、b の値を減らしたり、c の値を増やしたりします。さらに、a がオブジェクトの場合、このオブジェクトは新しく作成される可能性があり、a は参照です。どのような状況ですか?ここにはまだ多くの問題があります。変更数が変更されず、a の値が変更されない場合にのみ、a++ を追加することを検討してください。 , a++ 操作は、バージョン番号が同じ場合にのみ実行されます。これは、トランザクションのアトミック処理に似ています。


2. 競合がない場合でも、より多くの CPU リソースを消費し、無駄な作業を行います。

3. プログラムのテストが複雑になり、注意しないと問題が発生する可能性があります。


概要

CAS はロックを使用せずにアトミックな操作を実装するために使用できますが、ロックを導入せずに非常に単純な操作を行う場合は、非ブロックで操作を完了する必要がある場合は、CAS 操作の使用を検討することもできます。 CAS.複雑な操作に CAS を導入することはお勧めできません。これにより、プログラムが読みにくくなり、テストが困難になり、ABA の問題が発生します。

以上がJava での cas 操作の分析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート