>  기사  >  Java  >  Java 동시 프로그래밍 (8) 원자 변수 및 비차단 동기화 메커니즘

Java 동시 프로그래밍 (8) 원자 변수 및 비차단 동기화 메커니즘

巴扎黑
巴扎黑원래의
2017-06-26 09:18:481393검색

원자 변수 및 비차단 동기화 메커니즘

1. 잠금의 단점

1. 멀티 스레딩에서: 잠금 일시 중지 및 복구 과정에 많은 오버헤드가 있습니다. (시간이 지나면 현대 JVM이 언제 사용할지 판단하게 됩니다.) 정지) 시작, 회전 및 대기 시점)

  2.휘발성: 가벼운 수준의 동기화 메커니즘이지만 원자 복합 작업을 구축하는 데 사용할 수 없습니다.

  따라서: 경쟁을 관리할 때 보다 세분화된 수준을 가질 수 있는 방법이 필요합니다. 스레드 세부적으로는 휘발성 메커니즘과 유사하며 원자 업데이트 작업도 지원합니다

2. CAS

배타적 잠금은 비관적 기술입니다. 최악의 경우를 가정하므로 각 스레드는 배타적입니다

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... ...

3. 원자 변수 클래스

사용자 정의 개체일 수 있는 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&#39;t set upper to " + i + " < lower");
            IntPair newv = new IntPair(oldv.lower, i);if (values.compareAndSet(oldv, newv))return;
        }
    }
}

성능 문제: 중간 및 낮은 동시성(경쟁)에서 원자 변수를 사용하는 것이 더 좋습니다. 잠금 속도는 일반적으로 잠금 속도보다 빠릅니다.

4. 비차단 알고리즘

비차단 알고리즘은 많은 일반적인 데이터 구조에서 사용할 수 있습니다.

비차단 알고리즘: 멀티스레딩에서 작업 여부 성공 여부는 불확실하며 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를 감싸고 있습니다

5. ABA 문제

CAS 연산에서 자주 발생하는 문제:

  판단 value A인지, 그렇다면 업데이트 작업을 계속하여 B로 변경합니다.

 그러나 스레드가 A 값을 C로 변경한 다음 다시 A로 변경하면 이때 원본 스레드가 이를 판단합니다. A=A이고 업데이트 작업을 성공적으로 수행합니다.

A를 C로 변경한 다음 다시 A로 변경하는 작업을 변경으로 간주해야 하는 경우 알고리즘을 최적화해야 합니다

해결 방법: 버전 번호를 추가하고 업데이트합니다. 값이 동일하더라도 각 업데이트 작업의 버전 번호

   

위 내용은 Java 동시 프로그래밍 (8) 원자 변수 및 비차단 동기화 메커니즘의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.