자바 병렬 원자 조작 류 (AtomicLong 소스 코드 분석) 와 비 차단 알고리즘
7197 단어 동시 학습
배경
최근 몇 년 동안 병발 알고리즘 분야 의 대부분 연 구 는 비 차단 알고리즘 에 중심 을 두 었 다. 이런 알고리즘 은 바 텀 원자 기계 명령 (예 를 들 어 병발 교환 명령) 으로 자 물 쇠 를 대체 하여 데이터 가 병발 방문 에서 의 일치 성 을 확보 했다.비 차단 알고리즘 은 운영 체제 와 JVM 에서 스 레 드 / 프로 세 스 스케줄 링 체제, 쓰레기 회수 체제 와 잠 금 및 기타 병행 데이터 구 조 를 실현 하 는 데 광범 위 하 게 사용 된다.
자 물 쇠 를 바탕 으로 하 는 방안 에 비해 비 차단 알고리즘 은 디자인 과 실현 에 있어 복잡 하지만 그들 은 신축성 과 활약 성에 있어 큰 장점 을 가진다. 비 차단 알고리즘 은 여러 스 레 드 가 같은 데 이 터 를 경쟁 할 때 차단 되 지 않 기 때문에 입도 가 더욱 가 는 차원 에서 조 화 를 이 루 고 스케줄 비용 을 크게 줄 일 수 있다.자 물 쇠 는 자바 언어 잠 금 문법 이 비교적 간결 하지만 JVM 작업 과 자 물 쇠 를 관리 할 때 완성 해 야 할 작업 부족 이 간단 하지 않 습 니 다. 잠 금 을 실현 할 때 JVM 의 복잡 한 코드 경 로 를 옮 겨 다 녀 야 하고 운영 체제 급 잠 금, 스 레 드 연결 과 문맥 전환 등 작업 을 초래 할 수 있 습 니 다.
비 차단 알고리즘
잠 금 기반 알고리즘 에 서 는 여러 가지 활성 고장 이 발생 할 수 있 습 니 다. 스 레 드 가 잠 금 을 가지 고 있 을 때 I / O 가 막 혀 메모리 페이지 가 부족 하거나 다른 지연 으로 실행 되면 모든 스 레 드 가 계속 실행 되 지 못 할 수 있 습 니 다.어떤 알고리즘 에서 한 스 레 드 가 실패 하거나 걸 리 면 다른 스 레 드 도 실패 하거나 걸 리 지 않 습 니 다. 그러면 이 알고리즘 을 비 차단 알고리즘 이 라 고 합 니 다.만약 알고리즘 의 모든 단계 에 어떤 라인 이 실행 할 수 있다 면 이런 알고리즘 은 자물쇠 없 는 알고리즘 이 라 고도 부른다.만약 에 알고리즘 에서 CAS 를 스 레 드 간 의 조작 을 조율 하 는 데 만 사용 하고 정확하게 실현 할 수 있다 면 그 는 블록 없 는 알고리즘 이자 잠 금 없 는 알고리즘 이다.
자바 가 비 차단 알고리즘 에 대한 지원: 자바 5.0 부터 바 텀 은 원자 변수 류 (예 를 들 어 AtomicInteger 와 AtoMicReference) 를 사용 하여 효율 적 인 비 차단 알고리즘 을 구축 할 수 있 습 니 다. 바 텀 은 비교 및 교환 명령 (CAS) 을 사용 합 니 다.
비교 및 교환 (CAS)
CAS 는 읽 고 쓸 메모리 위치 V, 비교 할 값 A 와 쓰기 위 한 새 값 B 를 포함 하 는 세 개의 조작 수 를 포함한다.또한 V 의 값 이 A 와 같 을 때 만 CAS 는 원자 방식 으로 새로운 값 B 로 A 의 값 을 업데이트 합 니 다. 그렇지 않 으 면 어떠한 조작 도 수행 하지 않 습 니 다.V 의 값 이 A 인지 여부 와 상 관 없 이 V 의 원래 값 을 되 돌려 줍 니 다.CAS 의 의 미 는 V 의 값 은 A 라 고 생각 합 니 다. 만약 에 V 의 값 을 B 로 업데이트 하지 않 으 면 V 의 값 이 실제 얼마나 되 는 지 수정 하지 않 고 알려 주지 않 습 니 다.
원자 변수 클래스
원자 변수 (메모리 모델 에 대응 하 는 원자 성) 는 자물쇠 의 입도 보다 더 가늘다.양 급 이 더욱 가 볍 고 다 중 프로세서 시스템 에서 고성능 을 실현 하 는 병발 코드 에 있어 서 매우 관건 적 이다.원자 변 수 는 경쟁 이 발생 하 는 범 위 를 하나의 변수 로 축소 하 는데 이것 은 당신 이 얻 은 입도 가 가장 가 는 상황 입 니 다.원자 변 수 를 업데이트 하 는 빠 른 (비 경쟁) 경 로 는 잠 금 을 얻 는 빠 른 경로 가 느 리 지 않 고 빠 를 수 있 습 니 다. 그리고 느 린 경 로 는 잠 금 의 느 린 경로 블록 보다 빠 를 것 입 니 다. 그 는 스 레 드 를 걸 거나 다시 조정 할 필요 가 없 기 때 문 입 니 다.자물쇠 가 아 닌 원자 변 수 를 기반 으로 하 는 알고리즘 을 사용 할 때 스 레 드 가 실 행 될 때 지연 되 지 않 고 경쟁 에 부 딪 히 면 회복 되 기 쉽다.
자바 의 13 개 원자 조작 류
자바 는 JDK 1.5 부터 자바. util. concurrent. atomic 패키지 (이하 Atomic 패키지) 를 제공 합 니 다. 이 가방 의 원자 조작 류 는 간단 하고 성능 이 효율 적 이 며 스 레 드 가 변 수 를 안전하게 업데이트 하 는 방식 을 제공 합 니 다.변수의 유형 이 여러 가지 가 있 기 때문에 Atomic 패키지 에 모두 13 개의 종 류 를 제 공 했 고 4 가지 유형의 원자 업데이트 방식 에 속 합 니 다. 각각 원자 업데이트 기본 유형, 원자 업데이트 배열, 원자 업데이트 참조 와 원자 업데이트 속성 (필드) 입 니 다.Atomic 가방 의 종 류 는 기본적으로 Unsafe 로 이 루어 진 포장 류 입 니 다.
AtomicLong 소스 코드 분석
위의 4 가지 원자 유형 은 모두 CAS 를 바탕 으로 이 루어 지고 저층 은 unsafe 를 통 해 원자 조작 을 실현 한다.다음은 소스 코드 를 결합 하여 대표 적 인 AtomicLong 소스 코드 를 살 펴 보 겠 습 니 다.
초기 화
// AtomicLong , volatile
private volatile long value;
// value
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
// , 0
public AtomicLong(long initialValue) {
value = initialValue;
}
다음은 몇 가지 업데이트 방법 을 살 펴 보 겠 습 니 다.
// newValue,
public final long getAndSet(long newValue) {
return unsafe.getAndSetLong(this, valueOffset, newValue);
}
// , ,
public final boolean compareAndSet(long expect, long update) {
return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
// 1
public final long getAndIncrement() {
return unsafe.getAndAddLong(this, valueOffset, 1L);
}
// 1
public final long getAndDecrement() {
return unsafe.getAndAddLong(this, valueOffset, -1L);
}
// delta
public final long getAndAdd(long delta) {
return unsafe.getAndAddLong(this, valueOffset, delta);
}
위 에서 주로 사용 하 는 방법 을 열거 한 것 을 보면 대체적으로 unsafe. getAndAddLong 방법 을 호출 한 것 을 알 수 있 습 니 다. 그 다음 에 우 리 는 구체 적 으로 보 겠 습 니 다.
public native long getLongVolatile(Object var1, long var2);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
/*
unsafe.getAndAddLong(this, valueOffset, 1L)
var1
var2 value AtomicLong
*/
public final long getAndAddLong(Object var1, long var2, long var4) {
long var6;
do {
// var1 var2 ,
var6 = this.getLongVolatile(var1, var2);
// var6, var4, ,
// , , ,
} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6;
}
원본 코드 를 통 해 알 수 있 듯 이 현재 값 getLongVolatile 방법 을 얻 고 compare AndSwapLong 방법 을 비교 하고 교환 하 는 것 은 모두 native 방법 입 니 다.자바 로 원자 조작 을 실현 하 는 것 이 아니 라 구체 적 으로 여러분 은 바 텀 소스 코드 (c + +) 를 계속 볼 수 있 습 니 다. 여 기 는 깊이 있 지 않 습 니 다 (능력 유한).
비교 및 교환 결함
1. 소스 코드 를 통 해 알 수 있 듯 이 원자 업데이트 시 현재 값 을 먼저 획득 하고 현재 값 이 수정 되 지 않 은 후에 업데이트 작업 을 하 게 된다. 이것 은 경쟁 이 치열 하면 CAS 의 효율 이 자물쇠 보다 낮 을 수 있다 는 것 을 의미한다.구체 적 으로 각 학우 들 이 스스로 이해 할 수 있 도록 실현 하 다.
2. ABA 는 CAS 의 불가피 한 이 야 기 를 하 는 것 으로 비교 하고 교환 하면 이런 장면 이 존재 하고 변수 가 값 A 일 때 값 을 업데이트 합 니 다.그러나 실제 적 으로 다른 스 레 드 가 값 을 B 로 바 꾼 다음 에 값 을 A 로 바 꿀 수 있 습 니 다. 이때 업데이트 작업 을 성공 적 으로 수행 할 수 있 습 니 다 (과정 에 영향 을 주지 않 고 링크 와 같은 것 에 만족 하지 않 습 니 다).해결 방법 은 변수 에 버 전 번 호 를 입력 하고 버 전 번호 와 값 이 일치 해 야 업데이트 작업 을 수행 하 는 것 입 니 다 (AtomicStamped Reference 를 사용 할 수 있 습 니 다).
총결산
비 차단 알고리즘 은 밑 에 있 는 병렬 원 어 (예 를 들 어 잠 금 이 아 닌 비교 교환) 를 통 해 스 레 드 의 안전성 을 유지 합 니 다.이러한 밑바닥 의 원 어 는 원자 변수 류 를 통 해 밖으로 공개 되 는데 이런 종류 도 '더 좋 은 volatile 변수' 로 정수 와 대상 인용 에 원자의 업데이트 작업 을 제공한다.
참고 서적:
《 JAVA 병발 프로 그래 밍 실전 》.
《 JAVA 병발 프로 그래 밍 의 예술 》.