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 인터페이스를 사용하는 것이 더 적절합니다.