자바 volatile 키워드 역할 및 사용 장면 상세 설명

1.volatile 키워드 의 역할:변수의 가시 성(visibility)을 확보 합 니 다.volatile 키워드 에 의 해 수 정 된 변 수 는 값 이 변경 되면 다른 스 레 드 를 즉시 볼 수 있 고 더러 운 읽 기 현상 이 나타 나 지 않도록 합 니 다.다음 코드 세 션 과 같이 isShut Down 이 true 로 설정 되면 doWork 방법 은 실 행 됩 니 다.volatile 로 isShutDown 변 수 를 수식 하면 이 문 제 를 피 할 수 있 습 니 다.

public class VolatileTest3 {
 static class Work {
 boolean isShutDown = false;

 void shutdown() {
  isShutDown = true;
  System.out.println("shutdown!");
 }

 void doWork() {
  while (!isShutDown) {
  System.out.println("doWork");
  }
 }
 }

 public static void main(String[] args) {
 Work work = new Work();

 new Thread(work::doWork).start();
 new Thread(work::doWork).start();
 new Thread(work::doWork).start();
 new Thread(work::shutdown).start();
 new Thread(work::doWork).start();
 new Thread(work::doWork).start();
 new Thread(work::doWork).start();
 }
}
더러 운 읽 기 가 발생 했 을 때 실행 결 과 는 다음 과 같 습 니 다.

2.왜 더러 운 독 서 를 하 는가?
자바 메모리 모델 은 모든 변 수 를 메 인 메모리 에 존재 하도록 규정 하고 모든 스 레 드 는 자신의 작업 메모리 가 있 습 니 다.스 레 드 는 변수의 모든 작업 을 작업 메모리 에서 해 야 하 며,메 인 저장 소 를 직접 조작 할 수 없습니다.또한 모든 스 레 드 는 다른 스 레 드 의 작업 메모리 에 접근 할 수 없습니다.변수의 값 이 스 레 드 의 작업 메모리 에서 메 인 메모리 로 언제 돌아 올 지 확인 할 수 없습니다.
3.happens-before 규칙 의 이해 와 교정
인터넷 에서 volatile 키워드 와 관련 된 정 보 를 찾 을 때 여러 블 로 그 는 happens-before 원칙 을 언급 했다.개인 은 이 원칙 에 대한 이 해 는 이 volatile 변 수 를 조작 할 때 모든 순서 가 이 변수 에 대한 작업 이 완료 되 었 다(변 경 된 것 이 없 지만 메 인 저장 소 에 쓰 이지 않 은 경우)는 것 이다.모든 후속 적 인 이 변수 에 대한 작업 은 시작 되 지 않 았 다.그 뿐 입 니 다.
여기 서 나 는 인터넷 에서 흔히 볼 수 있 는 이론 이 이에 대한 이해 가 잘못 되 었 다 고 생각한다.다음 과 같다.이 관점 에 따 르 면 volatile 변수 flag 의 happens-before 원칙 으로 인해 A 스 레 드 2 곳 의 쓰기 동작 은 반드시 B 스 레 드 3 곳 의 읽 기 동작 보다 앞 선다.사실 이러한 관점 은 논리 적 결함 이 있 습 니 다.만약 에 C 스 레 드 가 존재 한다 면 먼저 flag 의 값 을 읽 은 다음 에 flag 의 값 을 기록 합 니 다.그러면 C 스 레 드 의 실행 시 기 는 무엇 입 니까?다른 D,E 라인 이 있다 면...이 코드 에 대한 정확 한 이 해 는 3 곳 에서 받 은 flag 가 true 라면 a 의 값 은 0 이 아니 라 1 이 어야 한 다 는 것 입 니 다.volition 수식 변수 때문에 프로세서 가 정렬 을 다시 하지 않 기 때문에 1 곳 에서 a 에 대한 할당 은 반드시 2 곳 에서 flag 에 대한 할당 전에 발생 합 니 다.flag 가 volatile 변수 가 아니라면 1 곳 과 2 곳 코드 의 실행 순 서 는 보장 되 지 않 습 니 다(프로세서 의 명령 재 정렬).대부분의 경우 1 이 2 보다 먼저 실행 되 지만.happens-before 원칙 은 다 중 스 레 드 가 같은 변수 에 대한 읽 기와 쓰기 작업 간 의 순서 가 아니 라 읽 기 작업 을 보장 할 때 이 변수 에 대한 모든 쓰기 작업 이 유효 합 니 다(메 인 저장 소 에 쓰기).
 

 
인증 은 다음 과 같 습 니 다:

public class VolatileTest {
 static class A {
 int a = 0;
 volatile boolean flag = false;

 void writer() {
  a = 1;   //1
  flag = true;  //2
  System.out.println("write");
 }

 void reader() {
  if (flag) {  //3
  int i = a;  //4
  System.out.println("read true");
  System.out.println("i is :" + i);
  } else {
  int i = a;
  System.out.println("read false");
  System.out.println("i is :" + i);
  }
 }

 }

 public static void main(String[] args) {
 A aaa = new A();
 new Thread(() -> aaa.reader()).start();
 new Thread(() -> aaa.writer()).start();
 }
}
실행 결 과 는 다음 과 같 습 니 다.쓰기 작업 이 실행 되 기 전에 읽 기 작업 이 완료 되 었 습 니 다.

 
 4.volatile 키워드 사용 필드
주의:volatile 은 변수의 가시 성 만 확보 할 수 있 고 volatile 변수 에 대한 원자 성 은 보장 할 수 없습니다.다음 코드 를 보십시오.

public class VolatileTest2 {
 static class A {
 volatile int a = 0;
 void increase() {
  a++;
 }
 int getA(){
  return a;
 }
 }

 public static void main(String[] args) {
 A a = new A();

 new Thread(() -> {
  for (int i = 0;i < 1000;i++) {
  a.increase();
  }
  System.out.println(a.getA());
 }).start();
 new Thread(() -> {
  for (int i = 0;i < 2000;i++) {
  a.increase();
  }
  System.out.println(a.getA());
 }).start();
 new Thread(() -> {
  for (int i = 0;i < 3000;i++) {
  a.increase();
  }
  System.out.println(a.getA());
 }).start();
 new Thread(() -> {
  for (int i = 0;i < 4000;i++) {
  a.increase();
  }
  System.out.println(a.getA());
 }).start();
 new Thread(() -> {
  for (int i = 0;i < 5000;i++) {
  a.increase();
  }
  System.out.println(a.getA());
 }).start();
 }
}
실행 결 과 는 다음 과 같 습 니 다.volatile 은 a+작업 의 원자 성 을 보장 할 수 없습니다.

volatile 의 정확 한 사용 방법 은 참고 할 수 있 습 니 다https://www.jb51.net/article/166888.htm
이상 이 바로 이번 지식 점 을 소개 하 는 모든 내용 입 니 다.여러분 의 지지 에 감 사 드 립 니 다.

좋은 웹페이지 즐겨찾기