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);
}를하면 동기화가 되고 루프가 멈춘다. 왜냐하면 println에 synchronized 키워드가 등장하기 때문!!!
근본적 해결방법은 volatile을 붙이기. static volatile boolean exitFlag = false; 이거 붙이면 메인 메모리와 작업 메모리를 동기화 시킨다.