메모리 가시성

이동건 (불꽃냥펀치)·2024년 12월 26일
0

Volatile 메모리 가시성

  public static void main(String[] args) {
         MyTask task = new MyTask();
         Thread t = new Thread(task, "work");
         log("runFlag = " + task.runFlag);
          t.start();
          sleep(1000);
          
          log("runFlag를 false로 변경 시도"); 
          task.runFlag = false; 
          log("runFlag = " + task.runFlag);
          log("main 종료");
     }
     static class MyTask implements Runnable {
       boolean runFlag = true;
         //volatile boolean runFlag = true;
         @Override
         public void run() {
			log("task 시작"); 
            while (runFlag) {
				// runFlag가 false로 변하면 탈출 }
			log("task 종료");
            }
	}
  • work 스레드는 runFlag를 사용해서 스레드의 작업을 종료한다.
  • runFlag값이 false가 되면 무한 루프를 탈출하며 작업을 종료한다.
  • main 스레드가 runFlag의 값을 false로 변경한다.
  • runFlag의 값이 false가 되었으므로 work스레드는 무한루프를 탈출하며 작업을 종료한다.
  • 하지만 실제로 실행해 보면 자바 프로그램이 멈추지 않고 계속 실행된다.
  • work스레드가 실행하는 while 조건은 false가 되었지만 while문을 빠져나오지 못했다.



  • cpu는 처리성능을 개선하기 위해 중간에 캐시메모리라는 것을 사용한다.
  • cpu 연산은 매우 빠르기 때문에 cpu 연산의 빠른 성능을 따라가려면 cpu 가까이에 매우 빠른 메모리가 필요한데, 이것이 바로 캐시 메모리이다.
  • 각 스레드가 runFlag의 값을 사용하면 cpu는 이 값을 효율적으로 처리하기 위해 먼저 runFlag를 캐시 메모리에 불러온다. 이후에는 캐시 메모리에 있는 runFlag를 사용하게 된다.
  • main 스레드의 runFlagfalse로 설정한다. 자연스럽게 main 스레드를 돌리는 cpu 코어의 캐시 메모리의 runFlag값이 false가 된다.
  • work 스레드가 사용하는 cpu 코어의 캐시 메모리는 여전히 값이 true이다.
  • 이때 캐시메모리에 있는 cpu값이 언제 메모리에 반영되어 false가 될지는 알 수 없다.
  • 이 뿐만이 아니라 메인 메모리에 반영된 값을 다시 가져와 work스레드가 사용하는 캐시메모리에 다시 불러와야 한다.



캐시 메모리를 사용하면 cpu 처리 성능을 개선할 수 있다. 하지만 여러 스레드에서 같은 시점에 정확히 같은 데이터를 보는 것이 중요할 수도 있다.

이를 해결하는 방안은 아주 간단하다. 성능을 포기하는 대신 값을 읽을 때 값을 쓸때
모두 메인 메모리에 직접 접근하면 된다.

자바에서는 vloatile이라는 키워드로 이런 기능을 제공한다.

 volatile boolean runFlag = true;
  • 이렇게 설정하면 runFlag에 대해서는 캐시메모리를 사용하지 않고, 값을 읽거나 쓸 때 항상 메인 메모리에 직접 접근한다.
  • 실행해 보면 runFlagfalse로 변경하자마자 task 종료가 출력되는 것을 알 수 있다.



자바 메모리 모델


메모리 가시성

멀티스레드 환경에ㅐ서 한 스레드가 변경한 값이 다른 스레드에서 언제 보이는지에 대한 것을 메모리 가시성이라한다. 이름 그대로 메모리에 변경한 값이 보이는 가 아닌가의 문제이다.


happens-before

happens-before 관계는 자바 메모리 모델에서 스레드간의 작업 순서를 정의하는 개념이다. 만약 A 작업이 B 작업보다 happend-before 관계에 있다면 A 작업에서의 모든 메모리 변경 사항은 B 작업에서 볼 수 있다. 즉 A 작업에서 변경된 내용은 B 작업이 시작되기 전에 모두 메모리에 반영된다.

  • happens-before 관계는 이름 그대로, 한 동작이 다른 동작보다 먼저 발생함을 보장하는 것이다.
  • happens-before 관계는 스레드간의 메모리 가시성을 보장하는 규칙이다.
  • happens-before 관계가 성립되면 한 스레드의 작업을 다른 스레드에서 볼 수 있게 된다.
  • 한 스레드에서 수행한 작업을 다른 스레드가 참조할 때 최신상태가 보장되는 것이다.

volatile 규칙

한 스레드에서 volatile 변수에 대한 쓰기 작업은 해당 변수를 읽는 모든 스레드에 보이도록 한다. 즉 volatiile 변수에 대한 쓰기 작업은 그 변수를 읽는 작업보다 happens-before관계를 형성한다.








출처: https://www.inflearn.com/course/%EA%B9%80%EC%98%81%ED%95%9C%EC%9D%98-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%B0%94-%EA%B3%A0%EA%B8%89-1/dashboard

profile
자바를 사랑합니다

0개의 댓글

관련 채용 정보