volatile 지시어는 Java에서 멀티스레딩 환경에서 변수의 가시성과 일관성을 보장하기 위해 사용되는 키워드입니다. volatile 키워드가 붙은 변수는 특정한 메모리 시멘틱을 가지며, 이를 통해 여러 스레드가 동시에 해당 변수에 접근할 때 일관된 값을 읽고 쓸 수 있도록 합니다.
volatile의 주요 특징과 작동 방식메인 메모리 접근:
volatile 변수는 항상 메인 메모리에서 직접 읽고 쓰여집니다. 일반적인 변수는 CPU 캐시에서 읽고 쓰여질 수 있지만, volatile 변수는 메인 메모리에서 읽고 쓰여지기 때문에 모든 스레드가 해당 변수의 최신 값을 볼 수 있습니다.
가시성 보장:
한 스레드가 volatile 변수에 쓰기를 수행하면, 그 값을 다른 스레드들이 즉시 볼 수 있습니다. 이는 쓰기 연산 후에 다른 스레드들이 즉시 최신 값을 읽을 수 있음을 의미합니다.
재정렬 방지:
컴파일러와 CPU는 코드 최적화를 위해 명령어의 실행 순서를 재정렬할 수 있습니다. 그러나 volatile 변수는 이런 재정렬을 방지하여, 변수의 읽기와 쓰기 순서가 보장됩니다. 이는 변수의 일관성을 유지하는 데 중요한 역할을 합니다.
public class VolatileExample {
private volatile boolean flag = false;
public void writer() {
flag = true; // 쓰기 연산
}
public void reader() {
if (flag) { // 읽기 연산
// flag가 true일 경우 수행할 작업
}
}
}
volatile을 사용하는 적절한 상황간단한 플래그나 상태 변수:
예를 들어, 여러 스레드가 접근하는 상태 플래그나 단순한 카운터 변수에 volatile을 사용할 수 있습니다.
Double-Checked Locking:
객체를 초기화할 때, 이중 검사 잠금(double-checked locking)을 사용할 때 volatile 키워드를 사용하여 안전한 초기화를 보장할 수 있습니다.
volatile 변수는 원자적 연산을 보장하지 않습니다.
예를 들어, volatile로 선언된 변수에 대해 i++ 같은 연산은 원자적이지 않기 때문에, 이런 경우는 AtomicInteger 같은 클래스를 사용하는 것이 좋습니다.
volatile 변수는 복잡한 상태를 관리하기에는 적합하지 않습니다. 복잡한 동기화가 필요한 경우 synchronized 블록이나 Lock 인터페이스를 사용하는 것이 더 적절합니다.