volatile 키워드 에서 동시 다발 문제 에 대한 개인 적 이해

5694 단어 volatile
 

    먼저 volatile 의 의 미 를 상세 하 게 분석 한 글 을 살 펴 보 자.
    http://www.infoq.com/cn/articles/java-memory-model-4
 
    volatile 키워드 의 의 미 를 물 어보 면 다음 과 같은 답 을 얻 을 수 있다.
    (1) volatile 에 대한 수식 변 수 는 각 스 레 드 간 의 쓰기 동작 이 다른 스 레 드 에 즉시 표 시 됩 니 다.
    (2) volatile 수식 변수의 조작 은 원자 적 이 며, volatile 의 할당 작업 이 그의 이전 값 에 의존 하면 원자 성 을 잃 게 된다. 
 
      사실은 (1) 더욱 깊 은 의 미 를 가진다. 즉, 가시 성에 대한 이해 이다.(2) volatile 자체 와 원자 성 은 직접적인 관계 가 없다. (2) 에서 말 한 원자 성 은 사실 진정한 의미 의 원자 성 이 아니 라 가시 적 인 또 다른 표현 형식 이다.
  
      그것 은 무엇이 가시 적 인 문제 입 니까? 같은 코드 에 대해 실행 라인 과 관찰 라인 이 보 이 는 실행 순서 가 반드시 일치 하 는 것 은 아 닙 니 다.듣 기 에 좀 까다롭다. 다음 과 같은 예 를 보 자.
   난 서 집행 으로 인 한 가시 적 문제
         다음 글 의 위조 코드 를 예 로 들 어 난 서 집행 으로 인 한 가시 적 인 문 제 를 설명 한다.
          스 레 드 1:
                       Resource resource=init();
                       boolean hasInit=true;
                       afterOperation();
          스 레 드 2:
                      if(hasInit){
                        getResource();
                       more operation;
                       }
          위 프로그램 은 대충 보 더 라 도 스 레 드 2 에서 hasInit = true 일 때 resource 는 반드시 초기 화 되 었 습 니까? 답 은 부정 적 입 니 다.
          JAVA 는 효율 성 을 위해 코드 가 실 행 될 때 어느 정도 명령 어 를 어 지 럽 히 지만 같은 스 레 드 내 프로그램 에 서 는 어 지 럽 습 니 다.
순서 실행 은 모든 코드 가 이전에 실 행 된 코드 에 영향 을 주지 않 습 니 다. 상기 프로그램 에 서 는 스 레 드 1 의 after Operation () 이 flag 값 을 true 로 가 져 왔 을 때 resource 는 초기 화 되 었 을 것 입 니 다. 그러나 스 레 드 2 에 서 는 resource 가 초기 화 되 지 않 았 을 때 true 인 flag 값 을 가 져 올 수 있 습 니 다.
         이전 volatile 의 의미 로 돌아 가면 다음 과 같 습 니 다.
  • volatile 변 수 를 쓸 때 JMM 은 이 스 레 드 에 대응 하 는 로 컬 메모리 의 공유 변 수 를 주 메모리 로 새로 고 칩 니 다.
  • volatile 변 수 를 읽 을 때 JMM 은 이 스 레 드 에 대응 하 는 로 컬 메모리 가 잘못 되 었 습 니 다. 스 레 드 는 다음 주 메모리 에서 공유 변 수 를 읽 습 니 다.
  •         쉽게 말 하면 상기 flag 변 수 를 volatile 로 설정 할 때 스 레 드 2 에 있어 resource 의 초기 화 는 반드시 volatile 이 쓰기 전에 실 행 됩 니 다. 즉, flag 의 쓰기 문 구 를 정의 하 는 위치 로 다시 배열 하 는 것 을 금지 합 니 다.
           스 레 드 2 에 있어 서 스 레 드 1 의 after Operation () 은 스 레 드 2 에 의 해 관찰 되 었 습 니 다. flag 가 이미 true 일 까요? 답 은 부정 적 입 니 다. 구체 적 인 원인 은 volatile 의 메모리 의미 두 번 째 조 를 참고 하 십시오. 즉, volatile 의 읽 기 가 필요 합 니 다.
          이상 은 글 의 첫머리 에서 말 하 는 "가시 성" 의 진정한 의미 입 니 다. 또한, 난 서 실행 은 컴 파일 된 명령 의 경우, JAVA 명령 한 마디 가 컴 파일 된 후의 실행 명령 도 난 서 에 의 해 실 행 될 수 있 습 니 다. 이 는 다음 에 언급 된 단일 모델 에서 설명 합 니 다.
         참고 로 다 중 스 레 드 상황 에서 코드 의 가시 성 을 어떻게 신속하게 판단 합 니까? 실제 응용 에 서 는 happens - before 원칙 을 참고 할 수 있 습 니 다.
     
       이상 은 사실 가시 적 인 문제 이다. 실제 응용 에서 가시 적 인 문 제 를 제외 하고 더욱 흔히 볼 수 있 는 것 은 원자 적 인 문제 이다.
     
       원자 조작 이 파괴 되다
            가장 간단 한 변수 부터 추가 작업 을 시작 합 니 다. JAVA 의 자체 증가 작업 은 원자 적 인 것 이 아니 라 는 것 은 잘 알려 져 있 습 니 다. JAVA 명령 한 마디 는 컴 파일 러 에 의 해 여러 명령 으로 분해 되 어 실 행 됩 니 다. 자체 증가 작업 의 경우,  a++  실행 중인 작업 의 바이트 번 호 는 다음 과 같 습 니 다.
           public void getNext(); Code: 0:   aload 0 / / 국부 변수 표 index 를 0 으로 불 러 오 는 변수 입 니 다. 여기 서 this 입 니 다.       1:   dup                 //현재 스 택 의 대상 을 참조 하여 복사 합 니 다.       2:   getfield        #2; / Field id: I, id 의 값 을 가 져 오고 그 값 을 창고 꼭대기 에 눌 러 줍 니 다.       5:   iconst_1            //int 형의 값 1 을 창고 꼭대기 에 눌 러 넣 기       6:   iadd                //스 택 꼭대기 두 int 형식의 요 소 를 더 하고 그 값 을 스 택 꼭대기 에 누 르 십시오.       7:   putfield        #2; / / Field id: I, 스 택 꼭대기 의 값 을 id 에 할당 합 니 다.       10:  return }
         만약 에 자체 증가 작업 을 수행 할 때 다른 스 레 드 가 같은 변수 a 에 대해 쓰기 작업 이나 읽 기 작업 을 하면 작업 대상 은 실행 과정 에서 상태 가 이상 한 변수 이 고 기대 밖의 결 과 를 얻 을 수 있 습 니 다.구체 적 인 자체 증가 작업 에 대한 다 중 스 레 드 테스트 는 인터넷 에 많다.
          원자 적 개념 은 쉽게 말 하면 열 조작 을 할 때 작업 중의 동작 이 실행 되 지 않 거나 모두 실 행 된 후에 야 실행 결 과 를 다른 스 레 드 에 볼 수 있다 는 것 이다.
          상기 개념 과 마찬가지 로 여러 개의 문 구 를 운송 할 수 있다. 예 를 들 어 한 방법 에서 다음 과 같은 write 작업 을 수행 해 야 한다.
           boolean flag=true;
           Date date=new Date();
          이 두 변수의 상 태 는 함께 업데이트 해 야 합 니 다. flag = true 가 실 행 된 후에 바로 다른 스 레 드 에서 읽 히 면 다른 스 레 드 에서 잘못된 date 변 수 를 얻 을 수 있 습 니 다.즉, 앞에서 말 한 '실행 과정 에서 상태 가 이상 한 변수' 이다.
         해결 방법 은 바로 상기 write 작업 과 read 작업 이 같은 자물쇠 로 synchronized 를 하 는 것 입 니 다.이렇게 하면 write 작업 을 할 때 정확 하지 않 은 값 이 다른 스 레 드 에서 읽 히 지 않 음 을 보증 할 수 있 습 니 다.
     
       원자 성 문 제 는 때때로 가시 적 인 문제 와 동시에 나타 나 는데 전형 적 인 대 표 는 바로 유명한 이중 잠 금 을 바탕 으로 하 는 단일 모델 이다.
      
    private Resource resource;
    public static Resource getInstance(){  
            if( resource == null ){   //(1)   
            synchronized(lock){      
                 if( resource == null ){    
                       resource = new Resource();  //(4) 
                 }  
             }  
         } 

     
     
         이 쓰기 의 resource 변 수 는 경쟁 자원 으로 4 로 표 시 된 문 구 는 실 패 를 일 으 킬 수 있 습 니 다.이 유 는 resource = new Resource () 라 는 말 에 최소 2 단계 가 포함 되 어 있 기 때 문 입 니 다. 하 나 는 resource 대상 의 구조 함 수 를 호출 하고 초기 화 하 는 것 입 니 다. 다른 하 나 는 새로 분 배 된 resource 대상 의 메모리 참조 할당 값 을 resource 변수 에 부여 하 는 것 입 니 다.다른 스 레 드 에서 문 구 를 실행 할 때 resource 를 volatile 로 설정 하지 않 았 기 때문에 비어 있 지 않 지만 초기 화 되 지 않 은 resource 대상 을 가 져 올 수 있 습 니 다.이것 이 바로 앞에서 말 한 난 서 집행 으로 인 한 문제 다.
         
      
           요약:      
                   병발 문 제 는 주로 자원 경쟁 시 원자 조작 이 파괴 되 고 다 중 스 레 드 상황 에서 무질서 하 게 집행 되 어 발생 하 는 스 레 드 가시 성 으로 나 뉜 다.
                  병발 과정 에서 상술 한 두 가지 문제 에 부 딪 히 면 모두 해결 방식 을 고려 해 야 한다.
                  흔히 볼 수 있 는 해결 방식 은 자 물 쇠 를 넣 어 실현 하 는 것 이다.자물쇠 의 획득 도 volatile 이 읽 는 메모리 의 미 를 가지 고 있 으 며, 자물쇠 의 방출 은 volatile 이 쓴 메모리 의 미 를 가지 고 있 습 니 다.만약 공력 이 부족 하 다 면, 다 중 스 레 드 프로 그래 밍 을 할 때 volatile 변 수 를 적 게 사용 하여 병발 문 제 를 해결한다.자 물 쇠 를 사용 하면 온당 하 다.
               
     
     
     

    좋은 웹페이지 즐겨찾기