이 기사는 주로 ABA 문제에 대한 내용과 Java의 회피에 관한 내용을 연구합니다.
"Java Concurrency 실제 실습"이라는 책 15 장에서 원자 변수를 사용하여 동시성 스택이 구현되며 코드는 다음과 같습니다.
공개 클래스 노드 {공개 최종 문자열 항목; 공개 노드 다음; 공개 노드 (문자열 항목) {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; pop () {node newtop; node OldTop; do {OldTop = top.get (); if (OldTop == null) {return null;} newTop = OldTop.next;} while (! top.compareAndset (Oldop, newtop)); return OldTop.item;}}}이 예제는 ABA 문제를 일으키지 않습니다. 왜 그렇지 않은지에 관해서는 나중에 설명하겠습니다. ABA 문제에 대해 먼저 이야기합시다.
ABA는 무엇입니까?
원래 책을 인용하십시오 : 알고리즘의 노드를 주기적으로 사용할 수 있다면 "비교 및 교환"명령을 사용할 때이 문제가 발생할 수 있습니다. CAS 작업에서 "V의 값은 여전히 a?"라고 판단되며, 그렇다면 업데이트 작업이 계속됩니다. 일부 알고리즘에서 V의 값이 먼저 A에서 B로, B에서 B로 변경되면 CAS가 성공적으로 작동합니다.
ABA의 예
때로는 ABA로 인한 결과는 매우 심각합니다. ABA가 어떤 문제를 일으킬 지 확인하려면 동시성 스택의 예를 수정하겠습니다.
공개 클래스 노드 {공개 최종 문자열 항목; 공개 노드 다음; 공개 노드 (문자열 항목) {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;} while (! top.com.pareAndset (oldtop, node)); {need) {need) {ned) OldTop; do {OldTop = top.get (); if (OldTop == null) {return null;} newTop = OldTop.next; timeUnit.seconds.slep (time);} while (! top.compareAndset (oldtop, newtop)); return oldtop;}}여기의 변경 사항에주의를 기울이면 Node는 기본적으로 변경되지 않았습니다.
ConcurrentStack의 변화에 중점을 둡니다
1. 푸시 방법 : 원래는 콘텐츠를 사용하여 노드를 구성하지만 이제 노드에서 직접 전달되며 "알고리즘의 노드를 재활용 할 수 있습니다".
2. 팝 방법의 수면은 결과를 관찰하기 위해 스레드의 실행을 시뮬레이션합니다.
먼저 두 개의 노드를 스택에 누릅니다.
ConcurrentStack Stack = New ConcurrentStack (); stack.push (새 노드 ( "a")); stack.push (새 노드 ( "b"));
그런 다음 두 개의 스레드를 만들어 스택을 입력하고 남겨 두는 작업을 수행합니다.
스레드 a 첫 번째 스태킹을 실행합니다 : 스택에서 Nodea를 꺼냅니다.
스택 .pop (3);
어떤 이유로, 스레드 A는 오랫동안 실행되었으며 3 초를 사용했습니다.
스레드 B는 스택을 실행 한 다음 스택으로 들어갑니다. 첫째, NODEA와 NODEB가 해제 된 다음 노드, NODEC 및 NODEA를 입력하게됩니다 (NODEA는 스택의 상단에 있습니다).
노드 a = stack.pop (0); stack.pop (0); stack.push (새 노드 ( "d")); stack.push (새 노드 ( "c")); stack.push (a);
참고 : 스레드 B는 노드의 재활용을 구현합니다. 먼저 스택의 모든 내용을 방출 한 다음 스택에 넣습니다. 마지막으로 스택 상단의 내용은 이전에 출시 된 노드입니다.
스레드 B가 이러한 작업을 수행 한 후, Thread A는 CAS를 실행합니다. 현재 CAS는 성공적으로 실행할 수 있습니다.
원래 아이디어에 따르면 스레드 A와 B가 실행 된 후 스택의 내용은 다음과 같아야합니다.
ABA 문제를 피하는 방법
ABA 문제를 해결하기 위해 AtomicStampedReference 및 AtomicMarkAbleReference가 Java로 제공됩니다.
AtomicStampedReference는 참조와 버전 번호의 두 가지 값을 원자 적으로 업데이트 할 수 있으며 노드의주기 사용량을 버전 번호로 구별 할 수 있습니다. 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 (node.next). node, v, v+1)); //} while (! top.compareAndset (oldtop, node, top.getStamp (), top.getStamp ()+1);} public node pop (int time) {node newTop; node OldTop; int v; do {v = top.getStamp (); if (if); if (gge). null;} newTop = oldtop.next; try {timeUnit.seconds.sleep (time);} catch (InterpruptedException e) {e.printstacktrace ();}} while (! top.compareAndset (Oldtop, newtop, V, V+1)); newTop, top.getStamp (), top.getStamp ())); return 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;요약
위의 것은 ABA 문제에 대한 간단한 토론과 Java의 회피에 대한이 기사의 모든 내용입니다. 모든 사람에게 도움이되기를 바랍니다. 관심있는 친구는이 사이트의 다른 관련 주제를 계속 참조 할 수 있습니다. 단점이 있으면 메시지를 남겨 두십시오. 이 사이트를 지원해 주신 친구들에게 감사드립니다!