1. 멀티 스레딩에서: 잠금 일시 중지 및 복구 과정에 많은 오버헤드가 있습니다. (시간이 지나면 현대 JVM이 언제 사용할지 판단하게 됩니다.) 정지) 시작, 회전 및 대기 시점)
2.휘발성: 가벼운 수준의 동기화 메커니즘이지만 원자 복합 작업을 구축하는 데 사용할 수 없습니다.
따라서: 경쟁을 관리할 때 보다 세분화된 수준을 가질 수 있는 방법이 필요합니다. 스레드 세부적으로는 휘발성 메커니즘과 유사하며 원자 업데이트 작업도 지원합니다
배타적 잠금은 비관적 기술입니다. 최악의 경우를 가정하므로 각 스레드는 배타적입니다
CAS는 비교하고 exchanges: CompareAndSwap/Set(A,B): 메모리의 값이 A라고 생각합니다. A이면 B로 수정하고, 그렇지 않으면 메모리의 원래 값을 반환하거나 수정 여부를 반환합니다. 성공
예: CAS 작업 시뮬레이션
//模拟的CASpublic class SimulatedCAS {private int value;public synchronized int get() {return value; }//CAS操作public synchronized int compareAndSwap(int expectedValue, int newValue) {int oldValue = value;if (oldValue == expectedValue) { value = newValue; }return oldValue; }public synchronized boolean compareAndSet(int expectedValue, int newValue) {return (expectedValue == compareAndSwap(expectedValue, newValue)); } }//典型使用场景public class CasCounter {private SimulatedCAS value;public int getValue() {return value.get(); }public int increment() {int v;do { v = value.get(); } while { (v != value.compareAndSwap(v, v + 1)); }return v + 1; } }
JAVA는 CAS 작업 제공
원자 상태 클래스: AtomicXXX의 CAS 메서드
JAVA7/8: 맵 작업: putIfAbsent,computerIfAbsent,computerIfPresent... ...
사용자 정의 개체일 수 있는 AtomicRefence 원자 업데이트 개체:
public class CasNumberRange {private static class IntPair {// INVARIANT: lower <= upperfinal int lower; //将值定义为不可变域final int upper; //将值定义为不可变域public IntPair(int lower, int upper) {this.lower = lower;this.upper = upper; } }private final AtomicReference<IntPair> values = new AtomicReference<IntPair>(new IntPair(0, 0)); //封装对象public int getLower() {return values.get().lower; }public int getUpper() {return values.get().upper; }public void setLower(int i) {while (true) { IntPair oldv = values.get();if (i > oldv.upper) {throw new IllegalArgumentException("Can't set lower to " + i + " > upper"); } IntPair newv = new IntPair(i, oldv.upper); //属性为不可变域,则每次更新新建对象if (values.compareAndSet(oldv, newv)) { //原子更新,如果在过程中有线程修改了,则其他线程不会更新成功,因为oldv与内存处值就不同了return; } } }//同上public void setUpper(int i) {while (true) { IntPair oldv = values.get();if (i < oldv.lower)throw new IllegalArgumentException("Can't set upper to " + i + " < lower"); IntPair newv = new IntPair(oldv.lower, i);if (values.compareAndSet(oldv, newv))return; } } }
성능 문제: 중간 및 낮은 동시성(경쟁)에서 원자 변수를 사용하는 것이 더 좋습니다. 잠금 속도는 일반적으로 잠금 속도보다 빠릅니다.
비차단 알고리즘은 많은 일반적인 데이터 구조에서 사용할 수 있습니다.
비차단 알고리즘: 멀티스레딩에서 작업 여부 성공 여부는 불확실하며 CAS
1을 통한 루프 실행 및 원자적 연산이 필요합니다. 위의 CasNumberRange
2. 스택의 비차단 알고리즘: 헤드 포인터와 하나의 상태만 저장합니다
//栈实现的非阻塞算法:单向链表public class ConcurrentStack <E> { AtomicReference<Node<E>> top = new AtomicReference<Node<E>>();public void push(E item) { Node<E> newHead = new Node<E>(item); Node<E> oldHead;do { oldHead = top.get(); newHead.next = oldHead; } while (!top.compareAndSet(oldHead, newHead));//CAS操作:原子更新操作,循环判断,非阻塞 }public E pop() { Node<E> oldHead; Node<E> newHead;do { oldHead = top.get();if (oldHead == null) {return null; } newHead = oldHead.next; } while (!top.compareAndSet(oldHead, newHead));//CAS操作:原子更新操作,循环判断,非阻塞return oldHead.item; }private static class Node <E> {public final E item;public Node<E> next;public Node(E item) {this.item = item; } } }
3 , Non -링크드 리스트의 차단 알고리즘: 헤드와 테일에 대한 빠른 액세스, 두 상태 저장, 더 복잡함
public class LinkedQueue <E> {private static class Node <E> {final E item;final AtomicReference<LinkedQueue.Node<E>> next;public Node(E item, LinkedQueue.Node<E> next) {this.item = item;this.next = new AtomicReference<LinkedQueue.Node<E>>(next); } }private final LinkedQueue.Node<E> dummy = new LinkedQueue.Node<E>(null, null);private final AtomicReference<LinkedQueue.Node<E>> head = new AtomicReference<LinkedQueue.Node<E>>(dummy);private final AtomicReference<LinkedQueue.Node<E>> tail = new AtomicReference<LinkedQueue.Node<E>>(dummy); //保存尾节点public boolean put(E item) { LinkedQueue.Node<E> newNode = new LinkedQueue.Node<E>(item, null);while (true) { LinkedQueue.Node<E> curTail = tail.get(); LinkedQueue.Node<E> tailNext = curTail.next.get();if (curTail == tail.get()) {if (tailNext != null) {// 处于中间状态,更新尾节点为当前尾节点的next tail.compareAndSet(curTail, tailNext); } else {// 将当前尾节点的next 设置为新节点:链表if (curTail.next.compareAndSet(null, newNode)) {/** * 此处即为中间状态,虽然在这里进行了两次原子操作,整体不是原子的,但是通过算法保证了安全: * 原因是处于中间状态时,如果有其他线程进来操作,则上面那个if将执行; * 上面if的操作是来帮助当前线程完成更新尾节点操作,而当前线程的更新就会失败返回,最终则是更新成功 */// 链接成功,尾节点已经改变,则将当前尾节点,设置为新节点 tail.compareAndSet(curTail, newNode);return true; } } } } } }
3. 원자 필드 업데이트
위 로직은 Node를 사용하여 링크드 리스트의 비차단 알고리즘을 구현합니다. 헤드 노드와 테일 노드를 저장하기 위해
실제 ConcurrentLinkedQueue에서는 Reflection 기반의 AtomicReferenceFiledUpdater를 사용하여 Node를 감싸고 있습니다
CAS 연산에서 자주 발생하는 문제:
판단 value A인지, 그렇다면 업데이트 작업을 계속하여 B로 변경합니다.
그러나 스레드가 A 값을 C로 변경한 다음 다시 A로 변경하면 이때 원본 스레드가 이를 판단합니다. A=A이고 업데이트 작업을 성공적으로 수행합니다.
A를 C로 변경한 다음 다시 A로 변경하는 작업을 변경으로 간주해야 하는 경우 알고리즘을 최적화해야 합니다
해결 방법: 버전 번호를 추가하고 업데이트합니다. 값이 동일하더라도 각 업데이트 작업의 버전 번호
위 내용은 Java 동시 프로그래밍 (8) 원자 변수 및 비차단 동기화 메커니즘의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!