2. CAS
1.コンセプト
CAS(compareAndSwap)、比較および交換
CAS操作では、古い値(操作前の値)と新しい値(操作する値)の2つの値を入力する必要があります。操作中に、最初に古い値が変更されたかどうかを比較します。変更がない場合は、新しい値に交換します。それ以外の場合は交換しません。
主にjava.util.concurrent
パッケージ内の複数のクラス(ロック、アトミック)の低レベルの実装に使用されます。コアクラスはsun.misc.Unsafe
、オペレーティングシステムの下部にあるアセンブリ命令を呼び出すため、クラス内のほとんどのメソッドがネイティブメソッドであるということです。
2.Javaはアトミック操作を実装します
ロックの使用に加えて、スピン+ CASを使用することもできます
スピンは、スレッドコンテキスト切り替えのオーバーヘッドを削減することです
3.アトミック操作のCAS実装の3つの主要な問題
[1] ABA問題
問題の説明:CAS操作には比較が必要なため。古い値が何度も変更されていて、最後に更新された値が今回比較された古い値である場合、更新されるべきではなかった状況が更新されます。
解決策:バージョン番号を使用して判断を追加します。値が同じかどうかを比較するだけでなく、バージョン番号が論理的かどうかも比較します
[2]長いスピン時間は、高いオーバーヘッドにつながります
問題の説明:CAS操作が失敗した場合、スピン時間が長くなりすぎて、CPUに非常に大きな実行オーバーヘッドが発生します(CPU使用率の増加)
解決策:JVMがプロセッサーによって提供される一時停止命令をサポートできる場合、効率が向上します。しばらく寝る
[3]共有変数のアトミック操作のみを保証します
問題の説明:CAS操作では、同時に1つの値しか比較できません。
解決策:ロックアップします。または、複数の共有変数を1つの共有変数に結合して、次のように操作します。java.util.concurrent.atomic.AtomicReference
4.java.util.concurrent.atomic関連クラス
【1】AtomicInteger
AtomicInteger atomicInteger = new AtomicInteger(10);
boolean compareAndSet = atomicInteger.compareAndSet(10, 20);
System.out.println(compareAndSet); // true
System.out.println(atomicInteger.get()); // 20
System.out.println(atomicInteger.incrementAndGet()); // 21
System.out.println(atomicInteger.decrementAndGet()); // 20
【2】AtomicReference
Object obj1 = new Object();
System.out.println("obj1:" + obj1);
AtomicReference<Object> atomicReference = new AtomicReference<Object>(obj1);
Object obj2 = new Object();
System.out.println("obj2:" + obj2);
Object getAndSet = atomicReference.getAndSet(obj2);
System.out.println("旧值:" + getAndSet); // obj1
System.out.println("新值:" + atomicReference.get()); // obj2
【3】AtomicStampedReference
Object obj1 = new Object();
int stamp = 1;
AtomicStampedReference<Object> atomicStampedReference = new AtomicStampedReference<Object>(obj1, stamp);
Object obj2 = new Object();
System.out.println("obj2:" + obj2);
stamp = 2;
boolean compareAndSet = atomicStampedReference.compareAndSet(obj1, obj2, 1, stamp);
System.out.println(compareAndSet); // true
System.out.println(atomicStampedReference.getReference()); // obj2
System.out.println(atomicStampedReference.getStamp()); // 2