王子のためのジャコウネコ
問題の詳細を説明する前に、短い物語を見てみましょう:
北宋の宋真宗皇后の死後、彼の最愛の側室である劉皇后と李皇后はともに妊娠しており、息子を産んだ方が正宮になる可能性が高いのは明らかでした。劉妃は長年嫉妬しており、李妃が男の子を産んで王妃にされるのではないかと恐れ、郭淮、宮司の杜唐、そして産婆のヨウシの協力を得て計画を立てた。出産時に昏睡状態で亡くなったが、なんとジャコウネコの毛皮が剥げてしまい、血まみれでテカテカになって、生まれたばかりの王子を連れ去ってしまったのだ。劉妃は宮廷侍女コウ・朱に王子を絞め殺すように命じたが、コウ・朱はそれに耐えられず、密かに宦官の陳林に引き渡し、陳林は王子をスーツケースに入れて八賢王のもとへ送った。彼を育てるために。さらに、皮を剥いだジャコウネコを見た真宗は、李妃が化け物を産んだのではないかと思い、李妃を寒宮に降格させた。やがて、劉妃が陣痛を迎えて男の子を出産したため、その子が王子となり、劉妃も王妃に任命された。予期せぬことに、6年後、劉女王の息子は病気で亡くなりました。鎮宗には後継者がいなかったため、兄の王八賢(実際にはその年に更迭された王子)の子を養子として迎え、皇太子に据えた。
この物語からわかるのは、王子は生まれたときにジャコウネコに置き換えられ、その後、奇妙なことが重なって最終的に王子に戻ったということです。結果は同じでも、その過程は紆余曲折あって、王子は本当に運命が悪い。
なぜこの話をするのか?実は、これは今日紹介する問題と大きく関係しています。同じ結果ですが、途中で何回操作が発生したか分かりませんが、変化がないと考えて良いでしょうか?さまざまなビジネス シナリオにおいて、この問題を慎重に検討する必要があります。
#ABA 問題の説明
マルチスレッド シナリオでは、CAS が表示されます
ABA 問題, ここに ABA 問題についての簡単な科学があります。たとえば、同じ値 (初期値は A) に対して CAS 操作を同時に実行する 2 つのスレッドがあります。3 つのスレッドは次のとおりです:
スレッド 1、期待値は A、更新される値は B スレッド 2、期待値は A、更新される値is B
スレッド 1 が最初に CPU タイム スライスを取得しますが、スレッド 2 は他の理由でブロックされています。スレッド 1 はその値を A の期待値と比較し、等しいことがわかり、値を B に更新します。すると、この時点でスレッド 3 が表示されます。期待値は B で、更新される値は A です。スレッド 3 は、値を期待値 B と比較します。値が等しいことが判明した場合、このとき、スレッド 2 はブロッキングから回復し、CPU タイム スライスを取得します。この値と期待値 A を比較し、等しい場合、値を B に更新します。スレッド 2 も操作を完了しましたが、スレッド 2 は値が A->B->A に変更されたことを知りません。
具体的な例を挙げてください
シャオミンは現金自動預け払い機の問題のため、現金自動預け払い機から50元を引き出しました。 2 つのスレッドがあり、同時に残高を 100 から 50 に変更します。
-
スレッド 1 (現金自動預け払い機): 現在の値 100 を取得し、それを 50 に更新することを期待します。 ; スレッド 2 (現金自動預け払い機): 現在値 100 を取得、50 に更新されることが予想されます; スレッド 1 は正常に実行されました、スレッド 2 は何らかの理由でブロックされています ; この時点で、誰かが Xiao Ming に 50 を送金します; スレッド 3 (デフォルト):現在の値 50 を取得し、それを 100 に更新することを期待します。この時点で、スレッド 3 が正常に実行され、残高は 100 になります。 スレッド 2 はブロックから回復し、次の値を取得します。 100。比較後、残高を 50 に更新し続けます。
この時点では、実際の残高は 100 (100-50 50)
であるはずですが、実際には 50 (100-50) になっていることがわかります。 50 -50)
これは、間違った提出結果をもたらす ABA の問題です。
解決策
ABAの問題を解決するには、バージョン番号を追加できます。 V は変更後、バージョン番号が 1
ずつ増加します。コード例
AtomicStampedReference による ABA の問題の解決
AtomicStampedReference は、オブジェクトの値とバージョン番号を内部的に維持します。AtomicStampedReference オブジェクトを作成するときは、初期値と初期バージョン番号を渡す必要があります。
-
AtomicStampedReference がオブジェクト値を設定する場合、書き込みが成功するには、オブジェクト値とステータス スタンプの両方が期待値を満たしている必要があります。
private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100,1); public static void main(String[] args) { //第一个线程 new Thread(() -> { System.out.println("t1拿到的初始版本号:" + atomicStampedReference.getStamp()); //睡眠1秒,是为了让t2线程也拿到同样的初始版本号 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.compareAndSet(100, 101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); atomicStampedReference.compareAndSet(101, 100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1); },"t1").start(); // 第二个线程 new Thread(() -> { int stamp = atomicStampedReference.getStamp(); System.out.println("t2拿到的初始版本号:" + stamp); //睡眠3秒,是为了让t1线程完成ABA操作 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("最新版本号:" + atomicStampedReference.getStamp()); System.out.println(atomicStampedReference.compareAndSet(100, 2019,stamp,atomicStampedReference.getStamp() + 1) + "\t当前值:" + atomicStampedReference.getReference()); },"t2").start(); }
1. 初期値 100、初期バージョン番号 1
2. スレッド t1 と t2 は同じ初期バージョン番号
3 を取得します。 t1 は ABA 操作を完了し、バージョン番号は 3 に増加しました
4. スレッド t2 は CAS 操作を完了し、最新のバージョン番号は 3 になりました。これは、スレッド t2 が以前に取得したバージョン番号 1 と等しくありません。操作は失敗しました
実行結果:#
t1拿到的初始版本号:1 t2拿到的初始版本号:1 最新版本号:3 false 当前值:100
#AtomicMarkableReference を通じて ABA 問題を解決する
AtomicStampedReference
バージョン番号をリファレンスに追加し、A -> B -> C -> D などのリファレンスの変更プロセス全体を追跡できます。 -> A. AtomicStampedReference を通じて、プロセス中に 3 回変更された参照変数を知ることができます。ただし、場合によっては、参照変数が何回変更されたかではなく、単に変更されたかどうかだけを気にすることがあります。したがって、AtomicMarkableReference
があります。
AtomicMarkableReference の唯一の違いは、参照を識別するために int を使用しなくなり、参照変数が変更されたかどうかを示すためにブール変数を使用することです。 <pre class='brush:php;toolbar:false;'>private static AtomicMarkableReference<Integer> atomicMarkableReference = new AtomicMarkableReference<Integer>(100,false);
public static void main(String[] args) {
// 第一个线程
new Thread(() -> {
System.out.println("t1版本号是否被更改:" + atomicMarkableReference.isMarked());
//睡眠1秒,是为了让t2线程也拿到同样的初始版本号
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicMarkableReference.compareAndSet(100, 101,atomicMarkableReference.isMarked(),true);
atomicMarkableReference.compareAndSet(101, 100,atomicMarkableReference.isMarked(),true);
},"t1").start();
// 第二个线程
new Thread(() -> {
boolean isMarked = atomicMarkableReference.isMarked();
System.out.println("t2版本号是否被更改:" + isMarked);
//睡眠3秒,是为了让t1线程完成ABA操作
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("是否更改过:" + atomicMarkableReference.isMarked());
System.out.println(atomicMarkableReference.compareAndSet(100, 2019,isMarked,true) + "\t当前值:" + atomicMarkableReference.getReference());
},"t2").start();
}</pre>
2. スレッド t1 と t2 は同じ初期バージョン番号を持ち、変更されていません false結果:3. スレッド t1 は ABA 操作を完了し、バージョン番号は true に変更されました
4. スレッド t2 は CAS 操作を完了し、バージョン番号は true に変更されましたが、これは取得したバージョン番号 false と等しくありません以前のスレッド t2 によって操作が失敗しました
以上是本期关于CAS领域的一个经典ABA问题的解析,不知道你在实际的工作中有没有遇到过,但是在面试中这块是并发知识考查的重点。如果你还没接触过此类的问题,我的建议是你自己将上面的代码运行一下,结合理论去理解一下ABA问题所带来的问题以及如何解决他,这对你日后的开发工作也是有莫大的帮助的!t1版本号是否被更改:false
t2版本号是否被更改:false
是否更改过:true
false 当前值:100
多说几句
以上が面接官は「ABA 問題とは何か知っていますか?」と尋ねます。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

MantisBT
Mantis は、製品の欠陥追跡を支援するために設計された、導入が簡単な Web ベースの欠陥追跡ツールです。 PHP、MySQL、Web サーバーが必要です。デモおよびホスティング サービスをチェックしてください。

SAP NetWeaver Server Adapter for Eclipse
Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。

VSCode Windows 64 ビットのダウンロード
Microsoft によって発売された無料で強力な IDE エディター

SublimeText3 英語版
推奨: Win バージョン、コードプロンプトをサポート!

ZendStudio 13.5.1 Mac
強力な PHP 統合開発環境
