자바 의 volatile 키워드 에 대한 정렬

4984 단어 Java
자바 에서 volatile 키 워드 는 주로 두 가지 역할 을 합 니 다.
(1)가시 성
(2)명령 재 정렬 금지
첫 번 째 가시 성 은 이해 하기 쉽다.바로 volatile 로 장 식 된 공유 변 수 를 사용 하 는 것 이다.만약 에 한 라인 이 값 을 수정 하면 다른 라인 에서 바로 볼 수 있다.원 리 는 volatile 변수 에 대한 읽 기와 쓰기 입 니 다.주 메모리 에서 스 레 드 작업 을 강제 합 니 다.
제2 조 명령 을 다시 정렬 하 는 것 을 금지 하고 국부 적 인 코드 가 실행 되 는 순 서 를 확보 할 수 있다.만약 에 우리 가 지금 다음 과 같은 코드 가 있다 고 가정 하면:
     int a=2;
     int b=1;

순서 적 으로 a 는 먼저 실행 해 야 하고 b 는 나중에 실행 해 야 한다.그러나 실제 적 으로 꼭 그렇지 는 않다.cpu 가 프로그램 을 실행 할 때 연산 효율 을 높이 기 위해 모든 명령 은 동시 다발 적 인 난 서 집행 이다.만약 에 a 와 b 두 변수 사이 에 의존 관계 가 없다 면 b 가 먼저 실행 하고 a 가 나중에 실행 할 수 있다.의존 관계 가 존재 하지 않 기 때문이다.그래서 누가 먼저,누가 나중에 프로그램의 최종 결과 에 영향 을 주지 않 는 다.이른바 지령 재 정렬 이다.ok,이어서 우 리 는 다음 에 약간 변 경 된 코드 를 계속 분석 합 니 다.
      int a=2;
      int b=1;
      int c=a+b;
     

이 코드 에서 a 와 b 가 어떻게 무질서 하 게 실행 되 든 c 의 결 과 는 3 이다.c 변 수 는 a 와 b 변수 에 의존 하기 때문에 c 변 수 는 a 나 b 로 정렬 되 기 전에 a 와 b 도 c 로 다시 배열 되 지 않 는 다.이것 은 happens-before 관계 의 단선 에서 as-if-serial 의 의미 에 의 해 제 한 된 것 이다.
이 안 에는 또 하나의 특수 한 상황 이 있 으 니 주의해 야 한다.
        int a = 1;
        int b = 2;

        try {
            a = 3;           //A
            b = 1 / 0;       //B
        } catch (Exception e) {

        } finally {
            System.out.println("a = " + a);
        }


위의 예 에서 a 와 b 변 수 는 의존 관계 가 없 지만 try-catch 블록 에서 정렬 이 발생 했 습 니 다.b 가 먼저 실행 한 다음 에 이상 이 발생 했 습 니 다.그러면 a 의 값 은 결국 3 입 니 다.JVM 은 정렬 이 이상 할 때 catch 블록 에서 관련 된 특수 처 리 를 하도록 보장 합 니 다.이 점 은 주의해 야 한다.
단일 스 레 드 환경 에서 명령 재 정렬 은 프로그램의 최종 실행 결과 에 영향 을 주지 않 습 니 다.그러나 재 정렬 은 다 중 스 레 드 환경 에서 프로그램 이 정상적으로 실행 하 는 데 영향 을 줄 수 있 습 니 다.아래 의 코드 를 보 세 요.
public class ReorderDemo1 {


    private int count=2;
    private boolean flag=false;
    private volatile boolean sync=false;

    public void write1()  {
        count=10;
        flag=true;//  volatile  ,      ,    flag=true   
    }

    public void read1()  {
        if(flag){
            System.out.print(count); //   jvm   10,  jvm   2,      
        }
    }


    public void write2() {
        count=10;
        sync=true;//      volatile,         
    }

    public void read2()  {
        if(sync){
            System.out.print(count); //  jdk5  , volatile  ,count      10
        }

    }




    public static void main(String[] args) {

        for(int i=0;i<300;i++){
            //     
            ReorderDemo1 reorderDemo1=new ReorderDemo1();
            //   
            Thread t1=new Thread(()-> { reorderDemo1.write1();});
            //   
            Thread t2=new Thread(()-> { reorderDemo1.read1(); });

             t1.start();
             t2.start();

        }




    }




}

위의 코드 에는 세 개의 구성원 변수 가 있 는데 그 중 마지막 하 나 는 volatile 로 장식 한 것 이 고 두 가지 방법 이 있 습 니 다.
첫 번 째 방법 중:
    private int count=2;
    private boolean flag=false;
    private volatile boolean sync=false;

    public void write1()  {
        count=10;
        flag=true;//  volatile  ,      ,    flag=true   
    }

    public void read1()  {
        if(flag){
            System.out.print(count); //   jvm   10,  jvm   2,      
        }
    }

위의 코드 는 명령 이 정렬 되 기 때문에 스 레 드 에서 write 1 방법 을 실행 하 는 flag=true 일 때 스 레 드 2 가 read 1 방법 을 실 행 했 습 니 다.그러면 count 의 값 은 확실 하지 않 습 니 다.10 일 수도 있 고 2 일 수도 있 습 니 다.이것 은 운영 체제 와 매우 큰 관계 가 있 습 니 다.예 를 들 어 cpu 가 명령 정렬 을 지원 하지 않 으 면 문제 가 발생 하지 않 습 니 다.예 를 들 어 X86 의 CPU 에서 코드 테스트 를 실행 하면 여러 개의 값 이 나타 나 지 않 을 수 있 지만 다른 운영 체제 도 나타 나 지 않 는 다 는 것 을 설명 할 수 없다.명령 재 정렬 은 다 중 스 레 드 환경 에서 불확실 성 을 가 져 올 수 있 습 니 다.정확하게 사용 하려 면 JMM 메모리 모델 을 이해 해 야 합 니 다.
두 번 째 방법 중:
    private int count=2;
    private boolean flag=false;
    private volatile boolean sync=false;
    
     public void write2() {
     count=10;
     sync=true;//      volatile,         
    }

    public void read2()  {
        if(sync){
            System.out.print(count); //  jdk5  , volatile  ,count      10
        }

    }


여기 서 sync 변 수 는 volatile 수식 을 추가 한 것 으로 정렬 을 금지 한 다 는 뜻 입 니 다.첫 번 째 스 레 드 가 write 2 방법 을 호출 할 때 똑 같이 두 번 째 스 레 드 가 read 2 방법 을 호출 할 때 sync=true 라면 count 의 값 은 반드시 10 입 니 다.어떤 친구 들 은 count 변 수 는 volatile 로 수식 하지 않 았 다 고 말 할 수 있 습 니 다.100%가시 성 을 어떻게 보장 합 니까?확실히 jdk 5 이전에 volatile 키 워드 는 이러한 문제 가 존재 합 니 다.반드시 volatile 수식 을 해 야 합 니 다.그러나 jdk 5 와 그 후에 이 문 제 를 복 구 했 습 니 다.즉,jsr 133 에서 volatile 키 워드 를 강화 한 것 입 니 다.volatile 변수 자 체 는 하나의 울타리 로 볼 수 있 고 그 전후의 변수 도 volatile 의 미 를 가지 도록 보장 할 수 있 습 니 다.또한 volatile 의 등장 으로 정렬 이 금지 되 어 있 기 때문에 다 중 스 레 드 에서 도 정확 한 결 과 를 얻 을 수 있 습 니 다.
요약:
자바 에 서 는 volatile 을 제외 하고 정렬 을 금지 하 는 기능 이 있 으 며,내 장 된 잠 금 synchronized 와 가방 을 보 내 는 Lock 도 같은 의 미 를 가진다.동기 화 수단 으로 해결 하 는 주요 문 제 는 코드 집행 의 원자 성,질서 성,가시 성 을 확보 하 는 것 이다.내 장 된 자물쇠 와 J.U.C 의 자 물 쇠 는 이 세 가지 기능 을 동시에 가지 고 있 으 며,volatile 은 원자 성 을 보장 할 수 없 기 때문에 필요 할 때 자물쇠 와 함께 사용 해 야 정확 한 다 중 스 레 드 응용 을 만 들 수 있 습 니 다.
jsr 133 상세 소개:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile

좋은 웹페이지 즐겨찾기