이전 글에서는 가시성 문제를 해결하기 위해 volatile 키워드를 사용한다고 하였다. 이번 글에서는 volatile 키워드를 통해 가시성을 보장하는 방법을 간단한 예시와 함께 설명하려고 한다.
가시성을 보장하지 못한 예제
public class Volatile {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(() -> {
int i = 0;
while (!stopRequested) {
i++;
}
});
backgroundThread.start();
Thread.sleep(1000);
stopRequested = true;
}
}
메인 스레드가 1초 후 stopRequested 변수를 true로 설정하기 때문에 backgroupThread는 1초 후 반복문을 빠져나올 것처럼 보인다. 그러나 실제로 실행하면 위 코드는 아래처럼 반복문을 오랜 시간 빠져 나오지 못하거나, 기타 다른 요인이 더 추가되면 영원히 못 나올 수도 있다.
가시성 문제가 발생한 원인과 해결 방법
다음 그림을 살펴 보자.
CPU 1에서 수행된 스레드를 backgroundThread, CPU 2에서 수행된 스레드를 mainThread라고 하자.
mainThread는 CPU Cache Memory 2와 RAM에 공유 변수인 stopRequested를 true로 쓰기 작업을 완료하였으나, backgroundThread는 CPU Cache Memory 1에서 읽은 업데이트 되지 않은 stopRequested 값을 사용한다. 이 값은 false이므로 계속해서 반복문을 수행하게 된다. 즉, mainThread가 수정한 값을 backgroundThread가 언제 보게 될지 보증할 수 없고 이러한 문제를 가시성 문제라고 한다.
이 문제를 해결하기 위해서는 stopRequested 변수를 volatile로 선언하면 된다. 그럼 다음 그림과 같이 CPU Cache Memory를 거치지 않고, RAM으로 직접 읽고 쓰는 작업을 수행하게 된다.
변경된 코드를 살펴보자.
public class Volatile {
private static volatile boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(() -> {
int i = 0;
while (!stopRequested) {
i++;
}
});
backgroundThread.start();
Thread.sleep(1000);
stopRequested = true;
}
}
코드를 실행시키면 1초 후 프로그램이 종료되는 것을 확인할 수 있다.
예상 면접 질문 및 답변
volatile 키워드에 대해 설명
Q.동시성 프로그래밍에서 발생할 수 있는 문제 중 하나인 가시성 문제를 해결하기 위해 사용되는 키워드 이다. 가시성 문제는 여러 개의 스레드가 사용됨에 따라, CPU Cache Memory와 RAM의 데이터가 서로 일치하지 않아 생기는 문제를 의미한다. volatile 키워드를 붙인 공유 자원은 RAM에 직접 읽고 쓰는 작업을 수행할 수 있도록 해준다.
참고