동기 데이터 구조의 원자 복합 류

6739 단어
더 읽 기
머리말
앞에서 소개 한 원자 업데이트 기 는 모두 하나의 대상 이나 필드 요 소 를 대상 으로 원자 업데이트 작업 을 할 뿐 입 니 다. 제1장 에서 언급 한 네 번 째 복합 형 원자 업데이트 기 는 하나의 인용 변 수 를 다른 변수 와 연결 시 켜 복합 적 인 원자 업데이트 작업 을 실현 할 수 있 습 니 다. 이런 업데이트 기 는 두 가지 가 있 습 니 다. Atomic MarkableReference, Atomic Stamped Reference 입 니 다.
 
AtomicStampedReference
Atomic Stamped Reference 는 CAS 작업 과정 에서 의 ABA 문 제 를 해결 하기 위해 int 형 표지 버 전 번호 와 유사 한 변 수 를 이원 조합 전체 로 인용 하기 때문에 Atomic Stamped Reference 는 버 전 스탬프 가 있 는 원자 인용 유형 이 라 고도 부른다.인용 형식 을 원자 업데이트 하려 고 할 때마다 최소한 가지 고 있 는 버 전 번 호 를 비교 해 야 합 니 다. 버 전 번호 가 일치 하지 않 으 면 업데이트 에 실 패 했 습 니 다. 물론 업데이트 할 때 해당 하 는 버 전 번호 도 함께 업 데 이 트 됩 니 다.소스 코드 로 분석 을 시작 하 는 게 좋 을 것 같 습 니 다.
public class AtomicStampedReference {
    //     Pair            stamp      
    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);
        }
    }
	
    //       pair   volatile  
    private volatile Pair pair;

	//    ,                   
    public AtomicStampedReference(V initialRef, int initialStamp) {
        pair = Pair.of(initialRef, initialStamp);
    }
	
	....
	
	private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe();
    private static final long pairOffset =
        objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class);

    //  pair       
    static long objectFieldOffset(sun.misc.Unsafe UNSAFE,
                                  String field, Class> klazz) {
        try {
            return UNSAFE.objectFieldOffset(klazz.getDeclaredField(field));
        } catch (NoSuchFieldException e) {
            NoSuchFieldError error = new NoSuchFieldError(field);
            error.initCause(e);
            throw error;
        }
    }
}

이상 의 소스 코드 를 통 해 알 수 있 듯 이 AtomicStamped Reference 내부 에 정적 내부 클래스 Pair 의 인 스 턴 스 변수 pair 가 있 습 니 다. AtomicStamped Reference 를 초기 화 할 때 들 어 오 는 초기 참조 변수 와 초기 버 전 번 호 를 저장 합 니 다. volatile 에 의 해 수 정 된 것 이 므 로 가시 성 을 확보 합 니 다.또한 원자 업데이트 작업 을 할 때마다 오래된 pair 대신 새로운 pair 인 스 턴 스 를 생 성 합 니 다. 즉, 진정한 인용 변수 와 버 전 번 호 를 하나의 전체 로 업데이트 하기 때문에 Atomic Stamped Reference 내부 에서 이 pair 변수의 오프셋 주 소 를 미리 가 져 와 원자 업데이트 작업 을 준비 합 니 다.
 
다음은 그 방법 을 보 자.그것 의 방법 은 두 가지 로 나 뉘 는데 하 나 는 get 방법 이 고 다른 하 나 는 원자 업데이트 방법 이다.
//             
public V getReference() {
	return pair.reference;
}
//        
public int getStamp() {
	return pair.stamp;
}
//            ,          int  ,
//  int             ,               。
//             。
public V get(int[] stampHolder) {
	Pair pair = this.pair;
	stampHolder[0] = pair.stamp;
	return pair.reference;
}

그 다음 에 가장 중요 한 원자 업데이트 작업 과 관련 된 방법 입 니 다.
//                  ,                    。    true
//       ,                       ,     happen-before    。
//                compareAndSet,          JVM          。
public boolean weakCompareAndSet(V   expectedReference,
								 V   newReference,
								 int expectedStamp,
								 int newStamp) {
	return compareAndSet(expectedReference, newReference,
						 expectedStamp, newStamp);
}
//CAS      ,                         ,          volatile   pair  ,
       ,   。    true
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)));
}

//                      。
public void set(V newReference, int newStamp) {
	Pair current = pair;
	if (newReference != current.reference || newStamp != current.stamp)
		this.pair = Pair.of(newReference, newStamp);
}
//                ,            。         ,         ,    true
public boolean attemptStamp(V expectedReference, int newStamp) {
	Pair current = pair;
	return
		expectedReference == current.reference &&
		(newStamp == current.stamp ||
		 casPair(current, Pair.of(expectedReference, newStamp)));
}
//      CAS         ,      unsafe CAS   pair         。    true
private boolean casPair(Pair cmp, Pair val) {
	return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val);
}

상기 원자 업데이트 방법 을 통 해 알 수 있 듯 이 AtomicStamped Reference 는 Unsafe 의 CAS 방법 + Volatile 키 워드 를 이용 하여 실제 인용 변수 와 int 의 버 전 번 호 를 저장 하 는 Pair 인 스 턴 스 를 업데이트 합 니 다.
 
AtomicMarkableReference
AtomicMarkableReference 는 사실 AtomicStamped Reference 의 특례 로 볼 수 있 습 니 다. AtomicMarkableReference 는 하나의 인용 변수 와 하나의 불 변 수 를 하나의 이원 으로 조합 하 는 것 이기 때문에 버 전 번 호 는 true 와 false 만 있 는 것 과 같 습 니 다. AtomicStamped Reference 처럼 모든 업데이트 에 버 전 번 호 를 기록 할 수 있 습 니 다.어떤 경우 에 우 리 는 우리 가 주목 하 는 대상 이 변경 되 었 는 지 알 아야 하기 때문에 변경 되 었 으 면 더 이상 변경 하지 않 습 니 다. 예 를 들 어 노드 에 삭 제 된 표 시 는 Atomic Markable Reference 를 통 해 우리 의 요구 에 도달 할 수 있 습 니 다.
 
Atomic MarkableReference 의 소스 코드 에 대해 서 는 Atomic Stamped Reference 의 디자인 원리 와 같 습 니 다. 표지 버 전의 int 형 변 수 를 불 형의 변수 로 바 꾸 었 을 뿐 정적 내부 클래스 Pair 로 이 인용 변 수 를 불 변수 와 조합 하여 원자 업 데 이 트 를 진행 하 였 습 니 다.그래서 그 소스 코드 는 더 이상 일일이 열거 하지 않 는 다.
public class AtomicMarkableReferenceTest {

	public static void main(String[] args) {
		AtomicMarkableReference amr = new AtomicMarkableReference("ABC", false);
		
		boolean result = amr.attemptMark("ABC", true);//   true
		
		System.out.println(result);
		
		System.out.println(amr.isMarked());//        ,true
		
		AtomicStampedReference  asr = new AtomicStampedReference("123", 0);
		
		boolean seted = asr.compareAndSet("123", "456", 0, 1);//           
		System.out.println(seted);
		
		System.out.println(asr.getStamp());//        1
	}

}

  
 
 
 
 

좋은 웹페이지 즐겨찾기