2014년 11월 17일 월요일

Java volatile 키워드에 관하여

Java로 프로그래밍 하면서 volatile 키워드를 사용해 본적이 있나요?
아마도 오픈 소스 프로젝트 코드에서 혹시 봤거나, 다른 예제 코드에서 본적은 있지만 직접 자신의 코드에서 잘 사용하지는 않는 키워드 같다.
문서로도 깔끔하게 정리되어 있지 않은 것 같고, 나 같은 경우에는 왜 필요한 건지 언제 써야 하는 건지도 잘 이해되지 않았던 것 같다.

volatile은 인스턴스 필드를 동기화(synchronization)하기 위한 키워드 이며 Java 5부터 지원되기 시작하였다.
그렇다면 인스턴스 필드 동기화는 왜 필요한가?

1. 멀티 프로세서를 사용하는 컴퓨터에서 각 프로세서는 메모리에 있는 값을 레지스터(register) 또는 로컬 캐쉬(local cache)에 임시 저장한다. 그렇게 되면, 서로 다른 프로세서에서 실행되는 쓰레드에서는 동일한 메모리 값에 대해서 서로 다른 값을 사용할 수도 있게 된다.

2. 컴파일러는 코드의 성능을 극대화하기 위하여, 작성된 코드의 순서를 재배열(code optimization)하는 일을 한다. 이때 컴파일러는 한가지 가정을 하는데, 그것은 "메모리의 값은 코드로 표현된 명시적인 인스트럭션(instruction)이 있을 때만 변경된다"는 것이다. 그러나 실제로는 다른 쓰레드에 의해서도 값은 변경될 수 있다. 즉 단일 쓰레드를 가정한 코드 최적화를 한다.

volatile 키워드를 사용하면 다음의 의미를 갖는다.
1. 인스턴스의 변수의 값은 로컬 쓰레드 내에 캐쉬 되지 않아야 한다.
2. 항상 메인 메모리에서 값을 읽거나 써야 한다.
3. 인스턴스 변수가 여러 쓰레드에 의해서 변경될 수 있음을 컴파일러에게 알려준다.
4. 암시적으로 인스턴스 변수가 synchronized 블럭에 감싸진 것 처럼 동작한다.

다음의 코드 예제를 보자.
private boolean done;
public synchronized boolean isDone() {
    return done;
}

public synchronized setDone(boolean done) {
    this.done = done;
}


done이라는 boolean 변수를 동기화하기 위해 synchroized 키워드를 사용하였다. 하지만 잘 알다시피 synchronized 키워드는 실행에 있어 꽤 비싼 비용을 요구한다. 즉 성능에 영향을 줄 수 있다는 뜻이다.

위의 코드는 아래와 같이 바꿀 수 있다.

private volatile boolean done;
public boolean isDone() {
    return done;
}

public setDone(boolean done) {
    this.done = done;
}

volatile 키워드는 final 변수에는 필요가 없다. 그리고 단일 쓰레드 환경에서도 필요가 없다. 단일 쓰레드 프로그래밍은 거의 없다고 해도 과언이 아니긴 하지만...

여기서 주의 할 것 하나는 volatile은 atomicity (원자성)을 제공하지 않는다는 것이다.
예를 들어, 아래 코드와 같이 값을 바꿔치기 하는 연산은 atomic 하지 않다. atomicity는 개발자가 직접 작성해야 한다.

public void flipOne { done != done } // not atomic

참고 Core Java Vol I 9th Edition

댓글 없음:

댓글 쓰기