ABA問題の理解
ABA問題の理解1 ABA問題の発生 2原子参照AtomicReference 3タイムスタンプ付き原子参照AtomicStampedReference ABA問題解決 1 ABA問題の発生
ABA問題とは,比較して交換するサイクルであり,時間差が存在するが,この時間差は予想外の問題をもたらす可能性がある.例えば、スレッド1とスレッド2は同時にメモリからAを取り出し、スレッドT 1は値をAからBに変更し、BからAに変更する.スレッドT 2が見た最終値はAであり,事前推定値との比較を経て両者は等しく更新可能であるが,このときスレッドT 2のCAS動作は成功したが,問題がないわけではない.
CASのように、頭と尾の一致だけを重視し、頭と尾が一致すれば受け入れる需要があります.しかし,プロセスを重視する需要もあり,途中で何の修正も起こらず,AtomicReference原子参照を引き出した.
2原子参照AtomicReference
AtomicIntegerは整数に対して原子操作を行い、AtomicIntegerは長整数型数に対して原子操作を行い、AtomicBooleanはブール型数に対して原子操作を行うが、実際にはこれらは全く足りない.POJOなら?このPOJOをAtomicReferenceで包装し,操作を原子化することができる.
Class AtomicReference,Valueは原子包装が必要な汎用クラスである.
例:
出力結果:
true User(userName=李四、age=23)false User(userName=李四、age=23)
では、私たちはどのように原子参照に基づいてABA問題を解決するか、タイムスタンプ付き原子参照AtomicStampedReferenceを見てください.
3タイムスタンプ付き原子参照AtomicStampedReference ABA問題解決
AtomicStampedReferenceクラスを使用するとABAの問題を解決できます.このクラスは「バージョン番号」Stamp」を維持し、CAS操作を行う場合、現在の値だけでなくバージョン番号も比較します.両方が等しい場合にのみ更新操作を実行します.
コアメソッド:
例:
出力結果:
=====以下の場合ABA問題の発生=====true 2019====以下の場合ABA問題の解決=====Thread 3第1次バージョン番号1//初期バージョン番号Thread 4第1次バージョン番号1//初期バージョン番号Thread 3第2次バージョン番号2//第1次修正後のバージョン番号Thread 3第3次バージョン番号3//第2次修正後のバージョン番号Thread 4修正がfalse現在の最新実のバージョン番号:3//修正に失敗し、この時T 4のバージョン番号は1+1であるが、実際のT 3はすでにバージョン番号を3に増加し、T 4修正に失敗したThread 4の現在の最新の実際値:100
T 3スレッドは1回目のバージョン番号を取得してから2秒間睡眠し、T 4スレッドが同じ初期バージョン番号を取得できることを保証します.
T 4スレッドは1回目のバージョン番号を取得してから4秒睡眠し、その間にT 3スレッドがABA操作を完了したことを保証する.
1番目のパラメータは推定値、2番目のパラメータは更新値、3番目のパラメータは推定バージョン番号、4番目のパラメータは更新バージョン番号を表します.推定値がメモリ実績値と等しく、推定バージョン番号が実績バージョン番号と等しい場合、更新メモリ値は更新値であり、更新バージョン番号は更新バージョン番号である.
ABA問題とは,比較して交換するサイクルであり,時間差が存在するが,この時間差は予想外の問題をもたらす可能性がある.例えば、スレッド1とスレッド2は同時にメモリからAを取り出し、スレッドT 1は値をAからBに変更し、BからAに変更する.スレッドT 2が見た最終値はAであり,事前推定値との比較を経て両者は等しく更新可能であるが,このときスレッドT 2のCAS動作は成功したが,問題がないわけではない.
CASのように、頭と尾の一致だけを重視し、頭と尾が一致すれば受け入れる需要があります.しかし,プロセスを重視する需要もあり,途中で何の修正も起こらず,AtomicReference原子参照を引き出した.
2原子参照AtomicReference
AtomicIntegerは整数に対して原子操作を行い、AtomicIntegerは長整数型数に対して原子操作を行い、AtomicBooleanはブール型数に対して原子操作を行うが、実際にはこれらは全く足りない.POJOなら?このPOJOをAtomicReferenceで包装し,操作を原子化することができる.
Class AtomicReference
例:
@Getter
@ToString
@AllArgsConstructor
class User {
String userName;
int age;
}
public class AtomicRefrenceDemo {
public static void main(String[] args) {
User z3 = new User(" ", 22);
User l4 = new User(" ", 23);
AtomicReference<User> atomicReference = new AtomicReference<>();
atomicReference.set(z3);
System.out.println(atomicReference.compareAndSet(z3, l4) + "\t" + atomicReference.get().toString());
System.out.println(atomicReference.compareAndSet(z3, l4) + "\t" + atomicReference.get().toString());
}
}
出力結果:
true User(userName=李四、age=23)false User(userName=李四、age=23)
では、私たちはどのように原子参照に基づいてABA問題を解決するか、タイムスタンプ付き原子参照AtomicStampedReferenceを見てください.
3タイムスタンプ付き原子参照AtomicStampedReference ABA問題解決
AtomicStampedReferenceクラスを使用するとABAの問題を解決できます.このクラスは「バージョン番号」Stamp」を維持し、CAS操作を行う場合、現在の値だけでなくバージョン番号も比較します.両方が等しい場合にのみ更新操作を実行します.
コアメソッド:
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(initialRef, initialStamp);
int stamp = atomicStampedReference.getStamp()
AtomicStampedReference.compareAndSet(expectedReference,newReference,oldStamp,newStamp);
例:
public class ABADemo {
static AtomicReference<Integer> atomicReference = new AtomicReference<>(100);
static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<>(100, 1);
public static void main(String[] args) {
System.out.println("===== ABA =====");
new Thread(() -> {
atomicReference.compareAndSet(100, 101);
atomicReference.compareAndSet(101, 100);
}, "Thread 1").start();
new Thread(() -> {
try {
// 1 ABA
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100, 2019) + "\t" + atomicReference.get());
}, "Thread 2").start();
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("===== ABA =====");
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t 1 " + stamp);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "\t 2 " + atomicStampedReference.getStamp());
atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "\t 3 " + atomicStampedReference.getStamp());
}, "Thread 3").start();
new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "\t 1 " + stamp);
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
boolean result = atomicStampedReference.compareAndSet(100, 2019, stamp, stamp + 1);
System.out.println(Thread.currentThread().getName() + "\t " + result + "\t :" + atomicStampedReference.getStamp());
System.out.println(Thread.currentThread().getName() + "\t :" + atomicStampedReference.getReference());
}, "Thread 4").start();
}
}
出力結果:
=====以下の場合ABA問題の発生=====true 2019====以下の場合ABA問題の解決=====Thread 3第1次バージョン番号1//初期バージョン番号Thread 4第1次バージョン番号1//初期バージョン番号Thread 3第2次バージョン番号2//第1次修正後のバージョン番号Thread 3第3次バージョン番号3//第2次修正後のバージョン番号Thread 4修正がfalse現在の最新実のバージョン番号:3//修正に失敗し、この時T 4のバージョン番号は1+1であるが、実際のT 3はすでにバージョン番号を3に増加し、T 4修正に失敗したThread 4の現在の最新の実際値:100
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
T 3スレッドは1回目のバージョン番号を取得してから2秒間睡眠し、T 4スレッドが同じ初期バージョン番号を取得できることを保証します.
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
T 4スレッドは1回目のバージョン番号を取得してから4秒睡眠し、その間にT 3スレッドがABA操作を完了したことを保証する.
atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(), atomicStampedReference.getStamp() + 1);
1番目のパラメータは推定値、2番目のパラメータは更新値、3番目のパラメータは推定バージョン番号、4番目のパラメータは更新バージョン番号を表します.推定値がメモリ実績値と等しく、推定バージョン番号が実績バージョン番号と等しい場合、更新メモリ値は更新値であり、更新バージョン番号は更新バージョン番号である.