728x90
Volatile 미적용


- work 스레드
- work 스레드는 반복문에서 count++ 을 사용해서 이 값을 계속 증가한다.
- 반복문을 1억 번 실행할 때마다 한 번씩 count의 값을 출력한다.
- flag 값이 false 가 되면 반복문을 탈출하고 count 값을 출력한다.
- main 스레드
- "main" 스레드는 1초간 대기하다가 flag 값을 false로 변경한다.
- flag 값을 false로 변경한 시점에 count 값을 출력한다
- main 스레드가 flag 를 false로 변경한 시점에 count 값은 555996592이다.
- work 스레드가 이후에 flag 를 확인하지만 아직 flag = true이다.
- work 스레드가 사용하는 캐시 메모리에서 읽은 것이다.
- work 스레드는 반복문을 계속 실행하면서 count 값을 증가시킨다.
- "work" 스레드는 이후에 count 값이 600000000 이 되었을 때 flag 가 false로 변한 것을 확인할 수 있다.
- 이 시점에 work 스레드가 사용하는 캐시 메모리의 flag 값이 false 로 변경되었다.
시점의 차이
- "main" 스레드가 flag를 false로 변경한 시점에 count 값은 555996592이다.
- "work" 스레드가 flag 값을 false로 확인한 시점에 Count 값은 600000000이다.
- 결과적으로 "main" 스레드가 flag 값을 false 로 변경하고 한참이 지나서야 "work" 스레드는 flag 값이 false 로 변경된 것을 확인한 것이다.
메모리 가시성(memory visibility)
캐시 메모리를 메인 메모리에 반영하거나, 메인 메모리의 변경 내역을 캐시 메모리에 다시 불러오는 것은 언제 발생할까?
이 부분은 CPU 설계 방식과 실행 환경에 따라 다를 수 있다. 즉시 반영될 수도 있고, 몇 밀리초 후에 될 수도 있고, 몇 초 후에 될 수도 있고, 평생 반영되지 않을 수도 있다.
주로 콘텍스트 스위칭이 될 때, 캐시 메모리도 함께 갱신되는데, 이 부분도 환경에 따라 달라질 수 있다. Thread.sleep() , 콘솔에 출력등을 할 때 스레드가 잠시 쉬는데, 이럴 때 컨텍스트 스위칭이 되면서 주로 갱신된다. 하지만 이것이 갱신을 보장하는 것은 아니다.
여기서 정확히 6억에서 변경된 flag 값을 읽을 수 있었던 이유는 6억에서 콘솔에 결과를 출력하기 때문이다. 콘솔에 결과를 출력하면, 출력하는 동안 스레드가 잠시 대기하며 쉬는데, 이럴 때 컨택스트 스위칭이 발생하면서 캐시 메모 리의 값이 경신된다.
참고로 이 부분은 주로 그렇다는 것이지 확실하게 캐시의 갱신을 보장하지는 않는다. 따라서 환경에 따라 결과가 달라질 수 있다.
결국 이 상황에서 메모리 가시성 문제를 확실하게 해결하려면 volatile 키워드를 사용해야 한다.
Volatile 적용 후


- "main" 스레드가 flag를 false로 변경하는 시점에 count 값은 129003878이다.
- "work" 스레드가 flag를 false로 확인하는 시점에 count 값은 129003878이다.
- 실행 결과를 보면 2가지 사실을 확인할 수 있다.
- main 스레드가 flag를 변경하는 시점에 work 스레드도 flag의 변경 값을 정확하게 확인할 수 있다.
- volatile을 적용하면 캐시 메모리가 아니라 메인 메모리에 항상 직접 접근하기 때문에 성능이 상대적으로 떨어진다.
- volatile 이 없을 때: 555996592 , 약 5.5억
- volatile 이 있을 때: 129003878 , 약 1.2억
- 둘을 비교해보면 물리적으로 약 5배의 성능 차이를 확인할 수 있다. 성능은 환경에 따라 차이가 있다.
728x90
'멀티스레드와 동시성' 카테고리의 다른 글
| Ch06. 동기화 - 동시성 문제 (0) | 2024.08.10 |
|---|---|
| Ch05. 메모리 가시성 - 자바 메모리 모델 (0) | 2024.08.08 |
| Ch05. 메모리 가시성 - volatile, 메모리 가시성 (0) | 2024.08.08 |
| Ch04. 스레드 제어와 생명 주기2 - yield (0) | 2024.08.08 |
| Ch04. 스레드 제어와 생명 주기2 - 프린트 예제 (0) | 2024.08.07 |