この記事は、主に、次のように、JavaのABAの問題と回避に関する関連する内容を研究しています。
本「Java Concurrency Practication」の第15章には、Atomic変数を使用して実装された並行性スタックがあり、コードは次のとおりです。
public class node {public final string item; public node next; public node(string item){this.item = item;}}} public class concurrentStack {AtomicReference <Node> TOP = new AtomicReference <Node>(); public void push(string item){node newtop = new node(item); node oldtop; do {oldtop = top.get(); newtop.next = oldtop;} newtop; node oldtop; do {oldtop = top.get(); if(oldtop == null){return null;} newtop = oldtop.next;} while(!top.compareandset(oldtop、newtop)); return OldTop.ITEM;}}}}この例はABAの問題を引き起こしません。なぜそうではないのかについては、後で説明します。最初にABAの問題について話しましょう。
ABAとは何ですか?
元の本を引用してください:アルゴリズムのノードを周期的に使用できる場合、「比較と交換」命令を使用するときにこの問題が発生する可能性があります。 CASの操作では、「Vの値はまだ?」と判断され、もしそうなら、更新操作は継続されます。一部のアルゴリズムでは、Vの値が最初にAからB、次にBからAに変化した場合、CASは正常に動作します。
ABAの例
時々、ABAによって引き起こされる結果は非常に深刻です。 ABAがどのような問題を引き起こすかを確認するために、Concurrency Stackの例を変更しましょう。
public class node {public final string item; public node next; public node(string item){this.item = item;}}} public class concurrentStack {AtomicReference <Node> top = new AtomicReference <Node>(); public void push(node node){node oldtop; do {oldtop = top.get(); node.next = oldtop;} oldtop; do {oldtop = top.get(); if(oldtop == null){return null;} newtop = oldtop.next; timeunit.seconds.sleep(time);} while(!top.compareandset(oldtop、newtop)); return OldTop;}}}}}ここの変更に注意してください、ノードは基本的に変更されていません
ConcurrentStackの変更に焦点を当てます
1.プッシュ方法:元々、コンテンツを使用してノードを構築していましたが、「アルゴリズムのノードをリサイクルできます」という要件を満たすノードを直接通過するようになりました。
2。結果を観察するためにスレッドの実行をシミュレートするPOPメソッドの睡眠。
最初に2つのノードをスタックに押し込みましょう。
concurrentStack stack = new concurrentStack(); stack.push(new Node( "a")); stack.push(new Node( "B"));
次に、2つのスレッドを作成して、スタックを入力して出発する操作を実行します
スレッドAは最初にスタッキングを実行します:ノデアをスタックから出してください
stack.pop(3);
何らかの理由で、スレッドAは長い間実行されており、3秒使用しています
スレッドBはスタックを実行してからスタックに入ります。最初に、NodeaとNodeBがリリースされ、次にノード、Nodec、Nodeaを入力します(Nodeaはスタックの上部にあります)
ノードa = stack.pop(0); stack.pop(0); stack.push(new Node( "d")); stack.push(new Node( "c")); stack.push(a);
注:スレッドBは、ノードのリサイクルを実装します。最初にスタック内のすべてのコンテンツをリリースし、次にスタックに入れます。最後に、スタックの上部にあるコンテンツは、以前にリリースされたノードです。
スレッドBがこれらのアクションを実行した後、スレッドAはCASを実行します。この時点で、CASは正常に実行できます。
元のアイデアによれば、スレッドAとBが実行された後、スタックのコンテンツは次のとおりです。CとD、Cはスタックの上部にありますが、ここでの実行の結果は、スタックに何もないということです。これはABAの問題です。
ABAの問題を回避する方法
ABAの問題を解決するために、JavaでAtomicStampedReferenceとAtomicMarkablereferenceが提供されています
AtomicStampedReferenceは、参照番号とバージョン番号の2つの値を原子的に更新し、バージョン番号ごとにノードのサイクル使用量を区別できます。 AtomicStampedReferenceの例を見てみましょう。
public class concurrentStack {AtomicStampedReference <Node> TOP = new AtomicStampedReference <Node>(null、0); public void push(node node){node oldtop; int v; do {v = top.getStamp(); oldtop = top.getReference(); node.next = oldtop;ノード、v、v+1); //} while(!top.compareandset(oldtop、node、top.getStamp()、top.getStamp()+1) null;} newtop = oldtop.next; try {timeunit.seconds.sleep(time);} catch(arternedexception e){e.printstacktrace();}} while(!top.compareandset(oldtop、newtop、v、v+1)); newtop、top.getStamp()、top.getStamp())); OldTop;} public void get(){node node = top.getReference(); while(node!= null){system.out.println(node.getitem()); node = node.getNode();}}}}注:コメント方法を使用することはできません。そうしないと、原子変数を単に使用することと違いはありません。
AtomicMarkableReferenceは、ブール型マーカービットと参照タイプを原子的に更新できます。次の例を参照してください。
AtomicmarkAblereference <Node> TOP = New AtomicMarkAblereference <Node>(null、true); public void push(node node){node oldtop; boolean v; do {v = top.ismarked(); oldtop = top.getreference(); node.next = oldtop;}要約します
上記は、JavaでのABAの問題と回避に関する簡単な議論に関するこの記事のすべての内容です。私はそれが誰にでも役立つことを願っています。興味のある友人は、このサイトの他の関連トピックを引き続き参照できます。欠点がある場合は、それを指摘するためにメッセージを残してください。このサイトへのご支援をありがとうございました!