소프트웨어적으로 동기화를 보장한다고 해도 기계어 레벨에서 다른 프로세스에 선점될 경우 데이터의 불일치가 발생할 수 있다. 그래서 하드웨어 기반 해결안 제시한다.
다른 블로그 글들을 읽어보면 이거는 하드웨어 기반인데 왜 소프트웨어 코드로 알려주지?
이런 의문증을 가졌는데, 기계어로 뚝 - 딱하면 사용할 수 있는 걸 프로그래머가 알아보기 쉽게 하기 위해서 소프트웨어 코드로 비슷하게 구현한 것이라고 한다.
"현재 락이 비어 있는지 확인하고, 동시에 내가 그 락을 차지"하는 걸 처리한다.
대표적으로 Spinlock 구현에 사용된다.
공유 자원을 보호하기 위해 락이 풀릴 때까지 반복해서(lock을 점검하며) 대기하는 동기화 방식
공유 변수에 접근하기 전에 lock의 true/false 상태를 확인한다.
Spinlock은 busy-wait를 하기 때문에, 짧은 시간이면 효율적이지만 긴 작업이면 CPU 낭비가 된다.
논블로킹 동기화 알고리즘의 핵심 연산으로,
락을 걸지 않고도 안전하게 공유 데이터를 수정할 수 있는 원자적 연산이다.
하나의 스레드만 공유 자원에 접근할 수 있도록 제한하는 락(lock) 메커니즘이다.
공유 자원에 접근할 수 있는 스레드의 수를 제한하기 위한 카운터 기반 동기화 도구이다.
변수의 값을 모든 스레드가 항상 메인 메모리에서 읽고 쓰도록 강제하는 동기화 수단이다.
즉, 단순한 읽기/쓰기 작업에 대해 ‘가시성’을 보장해준다.
멀티스레드 환경에서는 각 스레드가 자기 CPU 코어의 캐시에 있는 값을 사용할 수 있기 때문에,
다른 스레드가 변경한 값을 즉시 확인하지 못하는데 volatile 키워드는 이 문제를 해결해준다.
어떻게?
하드웨어 수준에서 캐시 무효화하면서 캐시 값이 없어진 스레드가 메인 메모리를 읽게 된다.
재정렬이 무엇인가?
컴퓨터는 더 빠른 실행을 위해서 코드 순서를 바꾸기도 함.
그래서 개발자가 작성한 코드의 순서와 실제 실행 순서가 다를 수도 있음.
volatile int count = 0;
count = 1; // 다른 스레드에서 이 값을 바로 볼 수 있음
volatile을 붙인 변수가 값을 변경하게 되면 다른 스레드에서도 항상 바로 확인할 수 있다.
count++;
하지만 volatile 키워드는 원시성을 보장하지 않기 때문에,
count++ 라는 복합연산(read → modify → write)은 동기화를 보장할 수 없다.
이런 경우는 AtomicInteger 사용해야 한다.
volatile은 가시성과 재정렬 방지는 보장하지만 원자성은 보장하지 않는다. 따라서 동기화를 부분적으로만 보장한다.
캐시 무효화란?