[JAVA] Volatile 변수
- Coding/Java
- 2022. 8. 3.
volatile 변수
volatile로 선언된 변수의 값을 바꿨을때 다른 스레드에서 항상 최신 값을 읽어갈 수 있도록 해준다.
▶ 변수의 값을 읽을때 CPU cache에 저장된 값이 아닌 Main 메모리에서 읽는다.
기존에는 CPU Cache에만 반영되고, 실제 Main Memory에는 반영되지 않는다.
Volatile은 Main Memory에 즉시 저장한다.
특정 변수를 선언할때 volatile 키워드를 지정하면, 컴파일러와 런타임 모두 '이 변수는 공유해 사용하고, 따라서 실행 순서를 재배치 해서는 안된다.' 라고 이해한다.
volatile 변수는 프로세서의 레지스터에 캐시되지도 않고, 프로세서 외부의 캐시에도 들어가지 않기 때문에 항상 다른 스레드가 보관해둔 최신의 값을 읽어갈 수 있다.
▶ 그러므로 다른 스레드라도 같은 메모리 주소를 참조하게되어, 최신 값을 읽어올 수 있다.
그러나 아무런 락이나 동기화 기능이 동작하지 않기 때문에 synchronized를 사용한 동기화보다는 강도가 약하다.
스레드 A가 volatile 변수의 값을 읽고나면 스레드 A가 변수에 값을 쓰기전에 볼 수 있었던 모든 변수의 값을 스레드 B에서도 모두 볼 수 있다. 따라서 메모리 가시성의 입장에서 본다면 volatile 변수를 사용하는 것과 synchronized 키워드로 특정 코드를 묶는게 비슷한 효과를 가져오고, volatile 변수의 값을 읽고나면 synchronized 블록에 진입하는 것과 비슷한 상태에 해당한다.
volatile 변수만 사용해서 메모리 가시성을 확보한 코드는 synchronized로 직접 동기화한 코드보다 훨씬 읽기가 어렵다.
volatile 변수 사용을 권장하는 경우
1) 변수에 값을 저장하는 작업이 해당 변수의 현재 값과 관련이 없거나, 해당 변수의 값을 변경하는 스레드가 하나만 존재하는 경우
쉽게 말해서, 멀티 스레드 환경에서 1개의 스레드만 read & write를 수행하고, 나머지 1개의 스레드에서 read하는 상황을 말한다.
volatile는 메모리 가시성은 해결하지만, race condition(여러개의 스레드가 동시에 경쟁)를 해결하지 못한다.
예시
스레드 A, 스레드 B가 있고, 변수 num이 있다.
num = 0에 대해서 num++를 한다고 할때, 스레드 A에서는 1, 스레드 B에서는 2가 나올거라고 예상한다. 하지만 이 결과는 그럴수'도' 있고, 아닐수도 있다.
num++는 단일 연산이 아닌, 아래의 3개의 연산을 수행한다.
1) 현재 값을 가져와서
2) 거기에 +1을 하고
3) 새 값을 저장한다.
스레드 A | 스레드 B |
read a from main memory(num=0) | |
read a from main memory(num=0) | |
num = num+1 | |
num = num +1 | |
save(num) => 1 to main memory | |
save(num) => 1 to main memory |
이러한 이유로 값을 변경하는 스레드가 1개일 경우에만 Volatile 변수 사용을 권장한다.
2) 해당 변수가 객체의 불변조건을 이루는 다른 변수와 달리 불변조건에 관련되어 있지 않은 경우
3) 해당 변수를 사용하는 동안에는 어떤 경우라도 락을 걸어 둘 필요가 없는 경우
Reference.
https://wjdtn7823.tistory.com/65
https://nesoy.github.io/articles/2018-06/Java-volatile
'Coding > Java' 카테고리의 다른 글
wait()과 notify(), notifyAll() (0) | 2022.08.17 |
---|---|
[JAVA] ThreadLocal (0) | 2022.08.04 |
[JAVA8 병렬프로그래밍] 원자적 변수 atomic (0) | 2022.06.17 |
[JAVA8 병렬프로그래밍] 분할반복 Spliterator (0) | 2022.06.16 |
[JAVA8 병렬프로그래밍] 스트림 병렬처리 (0) | 2022.06.16 |