자바 메모리 의 happens-before 와 정렬

happens-before 원칙 규칙:
프로그램 순서 규칙:하나의 스 레 드 에서 코드 순서에 따라 앞 에 쓰 는 작업 은 먼저 뒤에 쓰 는 작업 에서 발생 합 니 다.
잠 금 규칙:하나의 unLock 작업 은 같은 잠 금 에 대한 lock 작업 에서 먼저 발생 합 니 다.
volatile 변수 규칙:변수 에 대한 쓰기 동작 은 다음 변수 에 대한 읽 기 동작 에서 먼저 발생 합 니 다.
전달 규칙:만약 에 조작 A 가 조작 B 에서 먼저 발생 하고 조작 B 가 조작 C 에서 먼저 발생 하면 조작 A 가 조작 C 에서 먼저 발생 한 것 을 알 수 있다.
스 레 드 시작 규칙:Thread 대상 의 start()방법 은 이 스 레 드 의 모든 동작 에서 먼저 발생 합 니 다.
스 레 드 인 터 럽 트 규칙:스 레 드 인 터 럽 트()방법 에 대한 호출 은 인 터 럽 트 된 스 레 드 코드 에서 인 터 럽 트 사건 이 발생 한 것 을 먼저 감지 합 니 다.
스 레 드 종료 규칙:스 레 드 의 모든 작업 은 스 레 드 의 종료 검 측 에서 먼저 발생 합 니 다.우 리 는 Thread.join()방법 으로 끝 날 수 있 습 니 다.Thread.isAlive()의 반환 값 수단 으로 스 레 드 가 종료 되 었 음 을 감지 할 수 있 습 니 다.
대상 종료 규칙:한 대상 의 초기 화 완료 가 그의 finalize()방법의 시작 에서 먼저 발생 합 니 다.
우 리 는 위의 모든 규칙 을 상세 하 게 살 펴 보 자.
프로그램 순서 규칙:코드 가 단일 스 레 드 에서 실 행 된 결 과 는 질서 가 있 습 니 다.가상 컴퓨터,프로세서 가 명령 을 다시 정렬 하기 때문에 실행 결과 에 주의 하 십시오.정렬 을 다시 했 지만 프로그램의 실행 결과 에 영향 을 주지 않 기 때문에 프로그램의 최종 실행 결 과 는 순서대로 실 행 된 결과 와 일치 합 니 다.따라서 이 규칙 은 단일 스 레 드 에 만 효과 가 있 고 다 중 스 레 드 환경 에서 정확성 을 보장 할 수 없다.
잠 금 규칙:이 규칙 은 이해 하기 쉽 습 니 다.단일 스 레 드 환경 이 든 다 중 스 레 드 환경 이 든 하나의 잠 금 상태 에 있 으 면 unlock 작업 을 먼저 실행 해 야 lock 작업 을 할 수 있 습 니 다.
volatile 변수 규칙:이것 은 비교적 중요 한 규칙 으로 volatile 이 스 레 드 의 가시 성 을 확보 했다 는 것 을 상징 한다.통속 적 으로 말 하면 만약 에 한 라인 이 먼저 volatile 변 수 를 쓰 고 한 라인 이 이 변 수 를 읽는다 면 이 쓰기 동작 은 반드시 happens-before 읽 기 동작 일 것 이다.
전달 규칙:happens-before 원칙 을 제시 하여 전달 성 을 가진다.즉,A happens-before B,B happens-before C,그러면 A happens-before C.
스 레 드 시작 규칙:스 레 드 A 가 실행 과정 에서 ThreadB.start()를 실행 하여 스 레 드 B 를 시작 하면 스 레 드 A 가 공유 변수 에 대한 수정 은 다음 스 레 드 B 가 실 행 된 후에 스 레 드 B 를 볼 수 있 도록 합 니 다.
스 레 드 종료 규칙:스 레 드 A 가 실행 되 는 과정 에서 ThreadB.join()을 제정 하여 스 레 드 B 가 종 료 될 때 까지 스 레 드 B 가 종료 되 기 전에 공유 변수 에 대한 수정 은 스 레 드 A 가 돌아 오 기 를 기다 린 후에 볼 수 있 습 니 다.
위의 8 가 지 는 원생 자바 가 Happens-before 관 계 를 만족 시 키 는 규칙 이지 만 우 리 는 그들 에 게 happens-before 를 만족 시 키 는 다른 규칙 을 유도 할 수 있다.
1.라인 이 안전 한 대기 열 에 원 소 를 넣 는 동작 Happens-Before 대기 열 에서 이 원 소 를 꺼 내 는 동작
2.스 레 드 보안 용기 에 원 소 를 넣 는 작업 Happens-Before 용기 에서 이 요 소 를 꺼 내 는 작업
3.Countdown Latch 에서 의 마지막 동작 Happens-Before Countdown Latch\#await()동작
4.Semaphore 허 가 를 받 은 조작 Happens-Before 허가 획득
5.Future 가 표시 하 는 모든 작업 Happens-Before Future\#get()작업
6.Executor 에 Runnable 또는 Callable 작업 을 제출 합 니 다.Happens-Before 작업 을 시작 합 니 다.
여기 서 happens-before 의 개념 을 다시 한 번 말씀 드 리 겠 습 니 다.만약 에 두 조작 이 상기(앞의 8 개+뒤의 6 개)중 하나의 happens-before 규칙 이 존재 하지 않 는 다 면 이 두 조작 은 순서 적 인 보장 이 없고 JVM 은 이 두 조작 을 다시 정렬 할 수 있 습 니 다.A happens-before 조작 B 를 조작 하면 A 가 메모리 에서 하 는 조작 은 조작 B 에 대해 모두 볼 수 있 습 니 다.
위의 프로그램 순서 원칙 과 정렬 을 다시 하 는 것 사이 에 의문 이 있 습 니 다.아래 코드 를 보 세 요.

//  A:
context = loadContext();
inited = true;
//  B:
while(!inited ){
 sleep
}
doSomethingwithconfig(context);
스 레 드 A 의 동작 은 정렬 을 다시 할 수 있 습 니 다.스 레 드 B 의 context 초기 화가 불완전 합 니 다.그런데 왜 스 레 드 A 의 조작 은 정렬 을 다시 합 니까?happens-before 의 프로그램 순서 원칙 에 따라 context 의 초기 화 는 inited 앞 에 있어 야 하지 않 습 니까?내 가 이런 설명 을 볼 때 까지
1.만약 에 하나의 조작 happens-before 가 다른 조작 을 한다 면 첫 번 째 작업 의 실행 결 과 는 두 번 째 작업 에 대해 볼 수 있 고 첫 번 째 작업 의 실행 순 서 는 두 번 째 작업 전에 배열 된다. 
2.두 조작 사이 에 happens-before 관계 가 존재 하 는 것 은 반드시 happens-before 원칙 에 따라 제 정 된 순서에 따라 집행 해 야 한 다 는 것 을 의미 하지 않 는 다.정렬 을 다시 한 후의 실행 결과 가 happens-before 관계 에 따라 실 행 된 결과 와 일치 하면 이러한 정렬 은 불법 이 아니다.
       두 번 째 조 에 서 는 정렬 을 다시 한 실행 결과 가 happens-before 관계 에 따라 실 행 된 결과 와 일치 하면 정렬 을 다시 하 는 것 은 불법 이 아니 라 고 설명 했다.그래서 왜 스 레 드 1 에서 정렬 을 다시 하 는 지 설명 했다.스 레 드 1 의 두 작업 은 관련 이 없고 실행 결과 가 서로 의존 하지 않 는 다 는 것 이다.happens-before 의 관계 에 따라 실 행 됩 니 다.context 는 초기 화 되 고 inited 는 초기 화 됩 니 다.정렬 순서 로 실행 되 며,inited 는 초기 화 되 고,context 는 초기 화 됩 니 다.최종 결 과 는 모두 같 습 니 다.두 변수의 초기 화 입 니 다.그래서 정렬 을 다시 하 는 것 은 불법 이 아니다.이 는 프로그램 순서 규칙 이 다 중 스 레 드 에서 두 조작 간 에 관계 가 없 는 상황 에서 조작 이 다시 정렬 될 수 있 는 이 유 를 설명 한다.
       다른 한편,만약 에 두 조작 사이 에 관련 관계 가 존재 한다 면 프로그램의 실행 결과 가 바 뀌 지 않도록 as-if-serial 의 미 를 따라 야 한다.즉,컴 파일 러 와 프로 세 서 는 데이터 의존 관계 가 있 는 작업 에 대해 정렬 을 다시 하지 않 습 니 다.이 정렬 은 실행 결 과 를 바 꿀 수 있 기 때 문 입 니 다.그러나 작업 간 에 데이터 의존 관계 가 존재 하지 않 으 면 컴 파일 러 와 프로세서 에 의 해 정렬 될 수 있 습 니 다.
데이터 의존성 정의:
만약 에 두 작업 이 같은 변수 에 접근 하고 이 두 작업 중 하 나 는 쓰기 작업 이 라면 이 두 작업 사이 에 데이터 의존 성 이 존재 합 니 다.
데이터 의존 도 는 표 와 같이 다음 세 가지 유형 으로 나 뉜 다.
double pi = 3.14; // A
double r = 1.0; // B
double area = pi * r * r; // C
조작 A 와 C 사이 에는 데이터 의존성 이 있 고 B 와 C 사이 에 도 데이터 의존성 이 존재 하기 때문에 C 는 A 와 B 이전에 실행 할 수 없다.그러나 A 와 B 사이 에는 데이터 의존성 이 없어 A 와 B 사이 에 정렬 이 재 개 될 수 있다.
제어 의존성 정의:
아래 코드 를 살 펴 보고 스 레 드 1 은 writer 를 실행 하고 스 레 드 2 는 reader 를 실행 합 니 다.

class ReorderExample {
 int a = 0;
 boolean flag = false;
 public void writer() {
 a = 1; // 1
 flag = true; // 2
 }
 Public void reader() {
 if (flag) { // 3
  int i = a * a; // 4
  ……
 } 
 }
}
        우 리 는 조작 1 과 2 가 데이터 의존 성 이 없 으 면 정렬 이 재 개 되 어 프로그램 에 이상 이 생 길 수 있다 는 것 을 알 고 있다.그럼 3 과 4 사이 에 정렬 을 다시 하나 요?답 은 그 럴 거 야.여기에 새로운 개념 인 의존성 을 통제 하 는 것 이 있다.조작 3 과 조작 4 는 제어 의존 관계 가 존재 한다.코드 에 제어 의존성 이 존재 할 때 명령 시퀀스 실행 의 병행 도 에 영향 을 줄 수 있 습 니 다.이 를 위해 컴 파일 러 와 프로 세 서 는 추측(Speculation)을 통 해 상관 성 이 병행 도 에 미 치 는 영향 을 극복 할 것 이다.프로세서 의 추측 실행 을 예 로 들 면,라인 B 를 실행 하 는 프로 세 서 는 a*a 를 미리 읽 고 계산 한 다음,계산 결 과 를 정렬 버퍼(Reorder Buffer,ROB)라 는 하드웨어 캐 시 에 임시로 저장 할 수 있 습 니 다.조작 3 의 조건 이 사실 로 판단 되면 이 계산 결 과 를 변수 i 에 기록 합 니 다.
그림 에서 알 수 있 듯 이 실행 은 실질 적 으로 조작 3 과 4 에 대해 재 정렬 을 한 것 으로 추정 된다.정렬 을 다시 하면 다 중 스 레 드 프로그램의 의 미 를 파괴 합 니 다!

        단일 스 레 드 프로그램 에서 제어 의존 이 존재 하 는 작업 에 대해 정렬 을 다시 하고 실행 결 과 를 바 꾸 지 않 습 니 다(이것 도 as-if-serial 의미 에서 제어 의존 이 존재 하 는 작업 에 대해 정렬 을 다시 할 수 있 는 원인 입 니 다).그러나 다 중 스 레 드 프로그램 에 서 는 제어 의존 이 있 는 작업 에 대한 정렬 을 다시 하면 프로그램의 실행 결 과 를 바 꿀 수 있 습 니 다.
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

좋은 웹페이지 즐겨찾기