[CAS] CASの原則

1CASの原則

CASはすべてのアトミッククラスの基本原則であり、楽観的ロックは主にCASアルゴリズムを使用します。

CAS、比較および交換は、JDKが提供するノンブロッキングアトミック操作は、ハードウェアを介した比較更新操作のアトミック性を保証します。 通常、共有変数の原子性を確保するためにvolatileと組み合わせます。

アイデア:現在の変数の最新の値A(期待値)を取得してから、CAS操作を実行します。このとき、メモリ内の変数の値V(メモリ値V)が期待値Aと等しい場合は、他のスレッドによって変更されていないことを意味するので、変数値をB(更新)に変更します。値); Aでない場合、それ以上の変更はありません。

CAS操作はCPUの特別な命令を使用し、CPUはセキュリティの問題なしに一連の操作を完了するためのアトミック性を保証します。

CAS変数は、スレッド間の可視性を確保するためにvolatileで変更する必要があります。

CASアルゴリズムのアイデアのシナリオを使用する

  • 楽観的ロック
  • ConcurrentHashMapなどの並行コンテナ
  • 原子クラス

2AtomicLongでのCAS使用の分析

// 获取Unsafe实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
// 获取变量value在内存中的偏移量
private static final long valueOffset;
static {
    
    
    try {
    
    
        valueOffset = unsafe.objectFieldOffset
            (AtomicLong.class.getDeclaredField("value"));
    } catch (Exception ex) {
    
     throw new Error(ex); }
}

クラスのロードプロセス中に、安全でないインスタンスとvalueOffsetオフセットが最初にロードされます。valueOffsetは、メモリ内の変数のオフセットアドレスを表します。Unsafeは、メモリオフセットアドレスに従ってデータの期待値を取得し、CAS操作を実行します。得られた値が最新の値であることを確認するために、変数は通常volatileで変更されます。

public final long getAndIncrement() {
    
    
    return unsafe.getAndAddLong(this, valueOffset, 1L);
}
// Unsafe类
public final long getAndAddLong(Object var1, long var2, long var4) {
    
    
    long var6;
    do {
    
    
        // 通过对象地址和偏移量获取变量的最新值
        var6 = this.getLongVolatile(var1, var2);
        // 满足条件进行CAS操作
    } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

    return var6;
}

C ++ソースコードの実装の基礎となるCASメソッド

UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapLong(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jlong e, jlong x))
  UnsafeWrapper("Unsafe_CompareAndSwapLong");
  Handle p (THREAD, JNIHandles::resolve(obj));
  // 内存地址
  jlong* addr = (jlong*)(index_oop_from_field_offset_long(p(), offset));
  if (VM_Version::supports_cx8())
    // Atomic::cmpxchg原子性比较和替换
    return (jlong)(Atomic::cmpxchg(x, addr, e)) == e;
  else {
    jboolean success = false;
    ObjectLocker ol(p, THREAD);
    if (*addr == e) { *addr = x; success = true; }
    return success;
  }
UNSAFE_END

3CASのデメリット

3.1ABA問題

CASは比較値であり、値が等しい場合は変更されます。ここでそのような状況が発生する可能性があります。スレッド1は変数5の値を取得し、スレッド2は値を10に変更し、スレッド3は値を5に戻します。スレッド1の場合、変数の値は変更されていませんが、カウントなどの後続の操作では正しくありません。

分析:ABA問題は、変数の状態値の循環変換によって引き起こされます。変数の値が一方向にしか変換できない場合、それは円を形成せず、ABA問題は発生しません。

解決策:データベースの楽観的ロックの処理を参照し、バージョン番号を追加できます。変数が更新されると、バージョン番号が変更されます。バージョン番号を比較して、比較変数の値を置き換えます。コレクションのFast-Failメカニズムと同様に、modCount値が一貫しているかどうかを確認します。

3.2長いスピン時間はパフォーマンスコストをもたらします

例としてAtomicLongを取り上げます。同時実行性の高いシナリオでは、スレッドがCAS操作を実行できなかった場合、内部dowhileループは引き続きスピンしてCPUを消費します。

スピン理解

スピンは再試行戦略であり、楽観的ロック再試行戦略または悲観的ロック再試行戦略のいずれかです。たとえば、AticLongの多くのメソッドはgetAndAddLongメソッドを呼び出し、スピン+ CAS操作は内部で使用されます。ペシミスティックロックに関しては、ロックを取得するReentrantLockのタイムアウトメソッドであるtryLockは、forループを便利に使用します。

4安全ではない

UnsafeはCASのコアクラスです。Javaは、基盤となるオペレーティングシステムに直接アクセスすることはできませんが、ローカルメソッドを介してアクセスします。JDKのUnsafeクラスは、最下層のローカルメソッドを呼び出し、ハードウェアレベルのアトミック操作を提供します。

Unsafeクラスはrt.jarパッケージに属し、ブートストラップクラスローダーを使用してロードされますが、通常のメイン関数クラスはAppClassLoaderを使用してロードされます。Unsafeはメモリを直接操作するため、unsafeクラスと呼ばれ、任意に呼び出すことはできません。

安全でないインスタンスは、リフレクションを通じて取得できます。

Field field = Unsafe.class.getDeclareField("theUnsafe");
field.setAccessible(true);

この記事のケースコードの場所:https//gitee.com/dtyytop/advanced-java

おすすめ

転載: blog.csdn.net/LIZHONGPING00/article/details/114006951