기술 공부 끄적끄적/기타

멀티쓰레드에서 변수 공유하면 문제가 생기는 이유 (부제: 자바의 volatile 변수의 정체)

띠리링구 2024. 2. 3. 18:27

여러개의 쓰레드는 CPU에 있는 여러 개의 코어에서 동시에 돌아간다.

각 코어에는 L1, L2 캐시가 있고 CPU 코어 전체가 공유하는 L3 캐시가 있다.

캐시는 읽기 성능을 끌어올려준다는 장점이 있지만

모든 캐시에는 항상 sync 문제가 따라다닌다.

즉 캐시랑 메인 메모리의 데이터가 일치하지 않는 상황이 생길 수 있다는 것이다.

 

i라는 이름의 공유 변수가 존재한다고 치자.

boolean i = false;

 

thread1에서 i의 값을  true로 바꾼다.

i = true;

 

근데 이 i를 true를 바꾸는 문장이 실행돼도 메모리에서 i는 true로 안바뀌었을 수도 있다.

cpu cache에서만 i를 true로 바꾸고 메인메모리에 아직 반영이 안되었을수도 있기때문.

물론 메인메모리에 반영하는게 사람기준으로 봤을땐 엄청 짧은 찰나의 순간에 일어나지만

컴퓨터 기준에선 아니다. 그 찰나의 순간에 하필이면 결제 관련한 로직이 실행되고 있었어서 오류를 일으킨다면

송금이 제대로 안되고 뱅크런이 일어나서 은행은 문을 닫을수도 있다.

 

또 i가 메모리에서 true로 바뀌었다고 해도 i를 읽고있는 쓰레드는 메모리가 아니라 cpu cache에서 i를 읽고 있을수도 있다.

이 경우는 캐시에 아직 변경된값이 반영되어있지 않기때문에 문제가 발생할 수 있다.

 

이런 현상을 방지하기 위해 프로그래밍 언어들에서는 cpu cache를 안통하고 메모리에 있는 데이터를 직접 읽고 쓸수있는 기능을 제공하는데 이런 개념을 가시성(visibility)이라고 부르며 Java에서는 volatile 변수로 제공한다.

 

volatile boolean isComplete = false;

 

volatile 변수는 절대로 cpu cache를 통하지 않는다.

때문에 volatile 변수를 참조하면 그 값은 메모리에 있는 최신의 값임이 보장되며 이러한 특성때문에 자바의 동시성 관련 패키지에서는 volatile 변수를 거의다 이용한다.