JVM 메인 메모리와 작업 메모리

작업 메모리와 메인 메모리 동기화 이슈

  • 작업 메모리의 변화는 메인 메모리에 즉시 반영되지 않고 일정 시간 지연됨(일괄 처리에 따른 성능 향상)
  • 알려진 일반 변수의 동기화 시점
    • 명시적 동기화(synchronized, volatile)
    • Thread.start(), join() 호출
    • Lock, Atomic 클래스 사용
    • 클래스 로딩 과정에서 정적 변수 초기화 시
    • 기타 JVM이 정한 최적화, 동기화 기준 충족 시

작업 메모리 동기화 - 스레드 시작, 종료

  • 새로운 스레드 시작될 때 부모 스레드의 작업 메모리에 저장된 변수 값을 메인 메모리로 동기화
    • 만일 기존에 이미 실행중인 스레드가 있을 경우 이 시점에 동기화된 값을 확인 할 수 있음
  • 새로 시작된 스레드는 동기화가 완료된 메인 메모리에서 변수값을 로딩
public class Main {
    static boolean exitFlag = false;
 
    public static void main(String[] args) throws InterruptedException{
        System.out.println("[main] begin");
 
        MyThread myThread = new MyThread();
        Thread t1 = new Thread(myThread, "TestThread");
        t1.start();
 
        Thread.sleep(100);
        exitFlag = true;
        Thread.sleep(2000);
 
        System.out.println("[main] end, exitFlag: " + exitFlag);
    }
 
    public static class MyThread implements Runnable {
        @Override
        public void run(){
            System.out.println("MyThread.run() - begin");
            int counter = 0;
            while(!exitFlag) {
                ++counter;
            }
 
            System.out.println("MyThread.run() - end, exitFlag: " + exitFlag);
            System.out.println("MyThread.run() - end, counter: " + counter);
        }
    }
}

위 코드에서 t1 스레드에서 스태틱 전역 변수인 exitFlag을 read만 하고있다. JVM은 읽기만 하고있을때 이 변수의 메인 메모리와 작업 메모리 동기화는 고려하지 않는다. 그러므로 t1은 계속 실행된다.

그런데 여기서

while(!exitFlag) {
    ++counter;
    System.out.println(counter);
}

를하면 동기화가 되고 루프가 멈춘다. 왜냐하면 printlnsynchronized 키워드가 등장하기 때문!!!

근본적 해결방법은 volatile을 붙이기. static volatile boolean exitFlag = false; 이거 붙이면 메인 메모리와 작업 메모리를 동기화 시킨다.