1. 개요
본 포스팅은 김영한 강사님의 인프런 강의 "자바 고급 1편" 중 volatile 키워드에 대해 정리한 내용이다.
2. 멀티스레드 동작 방식과 메모리 가시성
다음 코드가 있다고 가정해보자.
package thread.volatile1;
import static util.MyLogger.log;
import static util.ThreadUtils.sleep;
public class VolatileFlagMain {
public static void main(String[] args) {
MyTask myTask = new MyTask();
Thread t = new Thread(myTask, "work");
log("runFlag = " + myTask.runFlag);
t.start();
sleep(1000);
log("runFlag를 false로 변경 시도");
myTask.runFlag = false;
log("runFlag = " + myTask.runFlag);
log("main 종료");
}
static class MyTask implements Runnable{
//변수를 메인 메모리에 직접 read, write
boolean runFlag = true;
@Override
public void run() {
log("task start");
while (runFlag) {
}
log("task end");
}
}
}
간단히 설명하자면 다음과 같다.
- 메인 스레드에서 MyTask를 구현한 스레드 t를 작동시킨다.
- myTask는 flag가 false가 될 때 까지 계속 작업을 수행한다.
- 메인 스레드는 t에서 동작하는 myTask의 flag를 false로 만들고 종료한다.
예상대로라면 메인스레드가 runFlag를 false로 만들 때 t는 종료되어야 하지만 실제로 돌려보면 그렇게 동작하지 않는다. 그 이유는 프로그램의 동작 원리를 보면 알 수 있다.

CPU에는 속도 향상을 위해 L1 캐시가 내장되어 있다. 참고로 그리진 않았지만 CPU 밖에 존재하는 캐시를 L2 캐시라 한다. 캐시는 저장소 중 속도가 레지스터 다음으로 높지만, 그만큼 가격이 비싸 위와 같은 방식으로 보통 사용한다.

이제 메인 스레드에서 flag를 false로 바꾸어보자. 각 코어는 캐시에 해당 변수가 없는 때에만 메인 메모리에서 값을 불러온다. 하지만 위와 같은 상황에서 t는 캐시에 있는 flag를 불러오므로 연산을 멈추지 않게 된다. 이처럼 한 스레드에서의 변경 사항이 다른 스레드에 언제 보이는지이 대한 문제를 memory visibility라 한다.
3. Volatile
public class VolatileFlagMain {
public static void main(String[] args) {
MyTask myTask = new MyTask();
Thread t = new Thread(myTask, "work");
log("runFlag = " + myTask.runFlag);
t.start();
sleep(1000);
log("runFlag를 false로 변경 시도");
myTask.runFlag = false;
log("runFlag = " + myTask.runFlag);
log("main 종료");
}
static class MyTask implements Runnable{
//변수를 메인 메모리에 직접 read, write
volatile boolean runFlag = true;
@Override
public void run() {
log("task start");
while (runFlag) {
}
log("task end");
}
}
}
위 코드는 flag에 키워드 volatile을 추가한 코드이다. 이를 실행하면 처음 의도했던대로 동작하는 것을 알 수 있다.

volatile은 해당 변수를 메인 메모리의 힙 영역에 넣고 직접 사용하겠다고 명시하는 키워드이다. 이를 사용하여 2에서 나온 문제를 해결할 수 있다.
하지만 위에서 언급했듯이 캐시는 성능 향상을 위한 설계이다. 따라서 volatile 키워드를 아무데나 사용하는 것은 올바르지 않다.
'Java' 카테고리의 다른 글
| [Java] BlockingQueue로 생산자-소비자 문제 해결하기 (0) | 2024.09.10 |
|---|---|
| [Java] 동시성 문제 해결하기 (0) | 2024.09.03 |
| Java Record (0) | 2024.08.31 |