2013년 1월 28일 월요일

Java에서 volatile 이란??



Java Language Specification 에 나온 내용을 기반으로 설명함.



8.3.1.4 volatile Fields


The Java programming language allows threads to access shared variables (§17.1).
As a rule, to ensure that shared variables are consistently and reliably updated, a
thread should ensure that it has exclusive use of such variables by obtaining a lock
that, conventionally, enforces mutual exclusion for those shared variables.
The Java programming language provides a second mechanism, volatile fields,
that is more convenient than locking for some purposes.
A field may be declared volatile, in which case the Java Memory Model ensures
that all threads see a consistent value for the variable (§17.4).8.3.1 Field Modifiers CLASSES

It is a compile-time error if a final variable is also declared volatile.



일단 위를 번역, 의역해보자..



자바는 thread 들이 공유된 변수에 접근하는 것을 허용한다.
일반적으로는(Java 뿐 아닌 다른 언어에 대해서도), Thread 간에 공유된 변수들의 일관성 있고 확실한 업데이트(데이터의 변경)를 하기 위해,

thread 는 이러한 변수들을 독점적으로 얻기 위한 방법으로 mutex 를 적용하곤 했다.

(여러 Thread 가 같은 변수에 한번에 접근하지 않기 위해서 Mutual Exclusion 처리를 해주고 있다는 말임.)


자바에서는 이에대해 좀더 쉬운 방법인 volatile field 라는 또 다른 방식을 제공한다.
field 가 volatile 을 이용해서 선언이 되었을 경우에,

자바 메모리 모델은 해당 변수에 대해 모든 쓰레드가 일관된 값을 사용할 것을 보장한다.
만약에 final 과 volatile 혼용해서 쓴다면 컴파일 시점에 오류가 발생하게 된다.



좀더 자세히 살펴보자..



1. Thread 간에 변수가 공유되면, Thread 는 그 값을 복사해 놓은 후, 복사한 값을 참조해서 사용한다.


Java 에서는 Thread 를 사용할 때 performance 향상을 위해 해당 변수의 값을

Thread 가 사용하는 메모리 영역에 caching 하는 것을 허용한다.


예를 들면, ThreadA 와 ThreadB 라는 두개의 쓰레드가 foo 라는 변수를 바라보고 있을 때,

foo 라는 변수를 각각 쓰레드의 로컬 메모리영역에 caching 해서 사용을 하는 식이 된다.


따라서, ThreadA 가 foo 라는 변수의 값을 "abc" 에서 "def" 로 바꾼다고 하더라도

어느 시점에는 ThreadB 는 여전히 "abc" 라는 값을 가지고 있을 수 있다.

(간단한 테스트 코드로도 확인 가능하다.)



이러한 문제를 극복하기 위해 volatile 이라는 키워드를 사용해서 변수를 선언하게 되면,

각 Thread 들은 foo를 caching 하지 않고, foo를 사용할 때마다 메인메모리 영역의 foo 를 바라본다.


위 글을 읽다 보면 final 과 volatile 은 같이 사용이 불가능 하다는 구문이 나오는데,

내 생각에는 final 은 변수가 바뀌지 않는다는 것을 전제도 하는 키워드이지만,

volatile 은 변수가 바뀌는 것을 전제로 하는 키워드이기 때문에 둘을 혼용해서 쓸 수 없는 것 같다.

2. Thread 간에 공유된 변수에 한 Thread 가 접근하면, 다른 Thread 는 접근하지 못한다.



예를들면 4바이트 이상의 변수인 long 을 변경한다고 했을 때, 앞 4바이트와 뒤 4바이트를 나눠서 변경한다.

예를들면 long 값을 21521542 에서 463634110 으로 변경시킨다고 했을 때, 앞 4바이트를 변경하고 뒤 4바이트를 변경한다.



이게 어떤 결과를 야기시킬 수 있냐면,

여러 ThreadA 가 long 값을 변경시키고 있는 와중에도 ThreadB 가 long 에 접근하면,

ThreadB 는 21521542 도 아니고 463634110 도 아닌 전혀 다른 값을 출력할 수 있다는 얘기다.



그 값의 정체는 앞4바이트 값은 바뀌고 있는데 뒤 4바이트는 예전의 값인, 두 숫자가 혼합된 값이 나올수도 있다는 얘기다.

(int 같은 경우에는 어차피 4바이트 변수니까 해당사항이 없긴 하다.)



내가 알아본 volatile 키워드의 효과는 위에 적은 두가지다.

까먹지 말고 잘 사용합시다~ :)

--------------------------------
출처 - http://maruldy.tistory.com/43