キャス
1. CASの紹介
CAS の完全な名前Compare And Swap
、比較および交換。CPU のアトミックな命令であり、最下層はハードウェアのアセンブリ命令に基づいて実装されます。CAS アルゴリズムには、3 つのオペランド内存值V
、预期原值A
、が含まれます新值B
。メモリ値 V が期待値 A と等しい場合、メモリ値 V を新しい値 B に更新します。
CAS の例:
例: CAS のアイデアを適用する場合i = 0 , i++
、スレッドは i 0 の値をメモリに読み込み、元の値 A=0 が期待されます。
V が A と等しい場合、このプロセスでi++
操作、V を新しい値 B (1) に変更します。
V が A と等しくない場合は、他のスレッドが i の値を変更していることを意味し、同時実行セキュリティの問題があります。この変更をあきらめて、もう一度やり直してください。
CAS 操作はアトミックです。複数のスレッドが CAS を使用して同時にデータを変更する場合、ロックする必要はありません。
2. CASの問題点
2.1 ABA問題
CAS は元の期待値 A が変更されたかどうかをチェックするもので、元の期待値 A が別の値 C に変更され、その後 A に戻された場合、スレッドはこの時点で CAS 比較を実行し、元の期待値 A が変更されていることを確認します。変わってないけど実際変わってる…
ABA 問題の本質は、CAS が初期と最終のみをチェックし、中間状態の変化を気にしないことです。
ABA の問題を解決する理由
シナリオを想定します: Xiaoming の口座に 100 元があり、50 元を引き出す予定です. 同時マルチスレッドの場合、スレッド A とスレッド B の両方が残高をチェックして 100 になります. スレッド A は正常に 50 を引き出します. 通常の状況では、スレッドBは失敗するはずですが、これはシャオミンのアカウントが50元を受け取った場合、比較のためにCASを使用するとスレッドBも成功し、さらに50元が差し引かれ、その結果、シャオミンは50を引き出しますが、100元を差し引きます。
ABA 問題の解決策
バージョン番号を使用して、変数の状態を識別します。たとえば、A1 を C2 に変更してから A3 に戻した場合、A1 と A3 が等しくなければ、ABA 問題は解決されます。
クラスを使用してAtomicStampedReference
ABA 問題を解決する
public class AtomicStampedReference<V> {
private static class Pair<T> {
final T reference; // 对象引用
final int stamp; // 用于标志版本号
private Pair(T reference, int stamp) {
this.reference = reference;
this.stamp = stamp;
}
static <T> Pair<T> of(T reference, int stamp) {
return new Pair<T>(reference, stamp);
}
}
/ **
* ......
*/
// pair使用volatile保证可见性
private volatile Pair<V> pair;
public boolean compareAndSet(V expectedReference, // 更新前的值
V newReference, // 更新后的值
int expectedStamp, // 预期的版本号
int newStamp) {
//更新后的版本号
// 获取当前元素值 + 版本号
Pair<V> current = pair;
return
expectedReference == current.reference && // 比较引用
expectedStamp == current.stamp && // 比较版本号
((newReference == current.reference && // 新引用等于旧引用
newStamp == current.stamp) || // 新版本号等于旧版本号
// 创建新的pair对象并CAS更新
casPair(current, Pair.of(newReference, newStamp)));
}
}
compareAndSet方法
以下の結果:
- 要素の値とバージョン番号が変更されておらず、新しいものと同じである場合、それは
CAS
更新されたことを意味し、戻りますtrue
- 要素の値とバージョン番号が変更されておらず、新しいものと異なる場合は、新しいものを作成し
Pair
、新しい値とバージョン番号に初期化します - その他の返品
false
2.2 共有変数のアトミック操作は 1 つだけ保証される
CAS 操作が複数の共有変数に対して実行される場合、2 つ以上の変数に対する操作やコード ブロックに対する操作など、操作の原子性は保証されません。
解決
Java1.5
最後に、参照されるオブジェクト間のアトミック性を確保するためのクラスJDK
が提供されAtomicReference
、アトミック操作のために複数の変数を 1 つのオブジェクトに配置できます。
コード ブロックの使用 synchronized
またはロックを選択できますLock
。
2.3 長いサイクル時間と高いオーバーヘッド
CAS
通常、ループ操作は連続してリトライするために使用されます (スピンロックなど)。時間が長い場合、CPU リソースのオーバーヘッドが増加します。
最適化
スピンの回数を設定できます。デフォルトは 10 回です。JVM
アダプティブ スピン ロックを提供し、サイクル数を自動的に調整し、同じロックのCAS
前回のCAS
実行に応じてCAS
回数を使用するかどうかを決定できます。CAS