잠 금 없 는 병렬 정책 (비교 교환)

5801 단어
비교 교환 은 자물쇠 가 없 는 병발 전략 으로 이런 방식 을 사용 하면 자물쇠 경쟁 이 가 져 오 는 시스템 비용 과 라인 간 의 빈번 한 스케줄 링 이 가 져 오 는 시스템 비용 을 피 할 수 있 기 때문에 자물쇠 보다 더 좋 은 성능 을 가진다.CAS 알고리즘 은 다음 과 같 습 니 다. CAS (V, E, N) 3 개의 매개 변 수 를 포함 하고 V 는 업데이트 할 변 수 를 대표 하 며 E 는 예상 치 를 대표 하고 N 은 새 값 을 대표 합 니 다.V 값 이 E 와 같 을 때 만 V 를 N 으로 설정 합 니 다. V 가 N 과 같 지 않 으 면 다른 스 레 드 가 V 를 작 동 했 음 을 설명 합 니 다. 현재 스 레 드 는 아무것도 하지 않 습 니 다.따라서 매번 하나의 스 레 드 만 성공 적 으로 작 동 하고 나머지 는 모두 실패 합 니 다. 실패 한 스 레 드 는 걸 리 지 않 습 니 다. 실 패 를 알 리 고 작업 이 성공 할 때 까지 계속 시도 합 니 다.  자바 및 패키지 에 atomic 패 키 지 를 제공 하 였 으 며, 그 안에 CAS 작업 의 스 레 드 안전 유형 이 구현 되 었 습 니 다.
1. 잠 금 없 는 스 레 드 안전 정수 (AtomicInteger) 내부 실현 분석 (jdk 1.8 기반):
private volatile int value;
value  AtomicInteger  , volatile  ,          

private static final long valueOffset;
valueOffset   value   AtomicInteger       ,  valueOffset     AtomicInteger  value     ,     :
static {
    try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
    } catch (Exception ex) { throw new Error(ex); }
}

상용 방법 예:
public final int getAndIncrement() {
    return unsafe.getAndAddInt(this, valueOffset, 1);
}
//    1,    1   

public final int getAndAdd(int delta) {
    return unsafe.getAndAddInt(this, valueOffset, delta);
}
//    delta,       

public final int getAndDecrement() {
    return unsafe.getAndAddInt(this, valueOffset, -1);
}
//    1,       

public final int addAndGet(int delta) {
    return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
    delta,    delta    

public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
         (expect)  ,          (update)

public float floatValue() {
    return (float)get();
}
      float 

이러한 유형의 방법 은 대체적으로 unsafe 를 사용 하여 실현 되 고 unsafe 중의 방법 은 1 층 은 c 로 실현 된다. 예 를 들 어:
public native boolean compareAndSwapInt(Object obj, long offset,int expect,int update);

유사 한 실현 은 AtomicLong, AtomicBoolean, AtomicReference 도 있 습 니 다. 더 이상 언급 하지 않 겠 습 니 다.
2. 존재 하 는 문제점:
  CAS 작업 을 사용 하면 현재 데이터 의 상 태 를 판단 할 수 없습니다. 어떤 스 레 드 가 value 를 수정 하면 다음 스 레 드 는 이전 스 레 드 가 수정 되 기 전의 값 을 수정 합 니 다. 그 후에 스 레 드 는 수정 되 었 는 지 모 릅 니 다.흔히 말 하 는 ABA 문제 다.  이러한 상황 에 대해 JDK 는 AtomicStamped Reference 류 를 제공 하고 내부 에서 대상 값 과 시간 스탬프 를 동시에 유지 합 니 다.Atomic Stamped Reference 대상 의 값 이 수정 되 었 을 때 대상 값 을 업데이트 하 는 것 외 에 시간 스탬프 를 동시에 업데이트 해 야 합 니 다.Atomic Stamped Reference 가 대상 값 을 설정 할 때 대상 값 과 시간 스탬프 는 모두 기대 치 를 만족 시 켜 야 기록 에 성공 할 수 있 습 니 다.따라서 대상 값 이 원래 값 으로 쓰 여 져 도 시간 스탬프 가 바 뀌 면 부적 절 한 기록 을 막 을 수 있다.
내부 실현 분석:
private volatile Pair pair;

pair 는 대상 값 과 시간 스탬프 를 저 장 했 습 니 다. Pair 류 소스 코드 는 다음 과 같 습 니 다.
private static class Pair {
    final T reference;  //   
    final int stamp;      //          ,    
    private Pair(T reference, int stamp) {
        this.reference = reference;
        this.stamp = stamp;
    }
    static  Pair of(T reference, int stamp) {
        return new Pair(reference, stamp);
    }
}

compare AndSet 방법 을 예 로 들 면:
 /**
  * Atomically sets the value of both the reference and stamp
  * to the given update values if the
  * current reference is {@code ==} to the expected reference
  * and the current stamp is equal to the expected stamp.
  *
  * @param expectedReference the expected value of the reference
  * @param newReference the new value for the reference
  * @param expectedStamp the expected value of the stamp
  * @param newStamp the new value for the stamp
  * @return {@code true} if successful
  */
public boolean compareAndSet(V   expectedReference,
                             V   newReference,
                             int expectedStamp,
                             int newStamp) {
    Pair current = pair;
        return
        expectedReference == current.reference &&
        expectedStamp == current.stamp &&
        ((newReference == current.reference &&
          newStamp == current.stamp) ||
         casPair(current, Pair.of(newReference, newStamp)));
}

당기 기대 치 는 현재 값 과 같 고 기대 시간 스탬프 와 현재 시간 스탬프 가 같 을 때 만 새 값 을 기록 하고 시간 스탬프 를 동시에 업데이트 합 니 다. casPair 방법 은 다음 과 같 습 니 다.
private boolean casPair(Pair cmp, Pair val) {
    return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}

3. 안전 하지 않 은 클래스 (unsafe)
  자바 는 운영 체제 의 밑바닥 에 직접 접근 할 수 없고 로 컬 방법 을 통 해 접근 할 수 있 습 니 다. 그 중에서 포인터 와 유사 한 조작 을 봉 인 했 습 니 다. unsafe 류 를 사용 하면 메모 리 를 분배 하고 메모 리 를 방출 할 수 있 습 니 다. 클래스 에서 3 개의 로 컬 방법 인 allocateMemory, reallocate Memory, freeMemory 는 각각 메모 리 를 분배 하 는 데 사 용 됩 니 다. 메모 리 를 확장 하고 메모리 unsafe 를 방출 하 는 주요 방법 은 다음 과 같 습 니 다.
//           int 
private native int getInt(Object o,long offset);
//           int 
private native int putInt(Object o,long offset,int x);
//            
private native long objectFieldOffset(Field f);
//       int ,  volatile  
private native void putIntVolatile(Object o,long offset,int x);
//        int ,  volatile  
private native int getIntVolatile(Object o,long offset);
// putIntVolatile  ,            volatile   
private native void putOrderedInt(Object o,long offset,int x);

  JDK 개발 자 들 은 이러한 종 류 를 사용 하 기 를 원 하지 않 습 니 다. unsafe 인 스 턴 스 를 얻 는 방법 은 공장 방법 getUnsafe () 를 호출 하 는 것 입 니 다. 이 를 실현 하 는 것 은 다음 과 같 습 니 다.
public static Unsafe(){
   Class cc=Reflection.getCallerClass();
   if(cc.getClassLoader()!=null)
      throw new SecurityException("Unsafe");
   return theUnsafe;
}

  이 방법 을 호출 할 때 이 방법 을 호출 하 는 클래스 를 검사 합 니 다. 이 클래스 의 ClassLoader 가 null 이 아니라면 이상 을 던 지고 접근 을 거부 합 니 다.

좋은 웹페이지 즐겨찾기