메모리 가시성(Memory Visibility)
- 멀티쓰레드 환경에서 쓰레드들은 각자의 캐시를 가지는 다른 프로세서에 할당되어 병렬 실행될 수 있습니다.
- 만약 쓰레드들이 하나의 변수를 공유하면, 각각의 프로세서 캐시가 변수의 복사본을 소유하게 됩니다.
- 이때 여러 프로세서(쓰레드)에 의해 캐시에 있는 공유 변수에 읽고 쓰기 동작이 수행되면 전체 프로그램 관점에서 보았을 때 캐시가 불일치(Cache inconsistency)하는 문제가 발생할 수 있습니다.
- 결과적으로 각 프로세서(쓰레드)가 동일한 공유 변수를 다른 값으로 바라 보게되는 문제가 발생할 수 있습니다.
Volatile 키워드 영향
- 컴파일러가 실행 최적화를 위해 코드 재배치(순서)를 수행하지 않습니다.
- 프로세서 내의 레지스터 또는 캐시에서 지역적으로 읽기, 쓰기 동작이 처리되지 않으며 항상 전역적인 메인 메모리에서 읽기, 쓰기 동작이 수행됩니다.
- 멀티프로세서 환경에서도 메인 메모리는 하나이기 때문에 여러 프로세서가 동시에 메인 메모리에 있는 공유 변수에 접근을 시도하더라도 공유 변수 접근은 동기화되어 차례로 진행되게 됩니다.
- 즉 하나의 공유 변수를 여러 쓰레드에서 읽고 쓰더라도 A 프로세서와 B 프로세서가 서로 다른 값을 바라보는 경우는 발생하지 않습니다. 왜냐하면 프로세서의 지역적 캐시가 아닌 전역적인 메인 메모리에서 모든 동작이 일어나기 떄문입니다.
- 그러나 단순한 읽기 쓰기가 아닌 i++ 과 같은 코드의 동기화를 제공해 주지는 못함으로 주의해야 합니다. 이런 경우에는 JAVA의 경우 synchrozied 키워드가 필요합니다.
이해에 도움을 준 컴퓨팅 원리
- 멀티프로세서 환경에서도 메인 메모리는 하나이기 때문에 여러 프로세서가 동시에 메인 메모리에 있는 공유 변수에 접근을 시도하더라도 공유 변수 접근은 동기화되어 차례로 진행되게 됩니다.
- 프로세서 캐시에 있는 데이터가 메인 메모리에 쓰여지는 두 가지 원리가 있습니다.
- Write Back : 쓰기 동작이 항상 캐시까지만 이루어진다. 주기억장치는 그 캐시 라인이 캐시로부터 제거(flush)될 때만 갱신된다.
- Write Through : 모든 쓰기 동작이 캐시뿐 아니라 주기억장치까지 이루어지며, 결과적으로 주기억장치의 내용도 항상 유효하다.
Volatile 필요한 케이스
아래와 같은 경우에 stop 변수가 두 쓰레드에서 공유되고, 루프를 중단하기 위해 사용된다면 stop 변수는 volatile 로 선언되어야 합니다. 만약 그렇지 않다면 루프 종료를 위해 외부에서 stop 을 true 로 바꾸어 주더라도 해당 루프는 상당한 지연 이후 종료되거나, 심지어 종료되지 않을 수도 있습니다.
volatile boolean stop;
...
while(!stop)
doSomthing();
참고