"왜 10만 원 + 10만 원 = 10만 원이 될까?"
우리가 볼 때 "예금 = 예금 + 10만 원"은 하나의 단순한 행동 같지만
컴퓨터(CPU) 입장에서는 이 작업이 3개의 아주 작은 단위로
쪼개져서 실행되기 때문이다.
읽기: 메모리에서 현재 예금(0원)을 CPU로 가져온다.
계산: 가져온 0원에 10만 원을 더한다.
쓰기: 계산된 10만 원을 다시 메모리에 저장한다.
[왜 문제가 터질까?]
스레드 T1이 읽기와 계산까지만 하고 아직 쓰기를 하기 직전
(메모리에는 여전히 0원인 상태)에
스레드 T2가 읽기를 실행해 버린다.
그러면 T2도 0원을 읽어 가고, 결국 두 스레드 모두 0원에 10만 원을 더한 뒤
각각 '10만 원'을 메모리에 덮어쓰게 된다.
이렇게 실행 순서에 따라 결과가 망가지는 것을 경쟁 상태(Race Condition)라고 한다.
"분명 잠금 코드를 짰는데, 왜 잠금이 뚫릴까?"
책을 보면 while(lock == true); 로 남이 사용 중인지 확인하고,
lock = true; 로 내가 잠금을 거는 코드가 나온다.
뭔가 완벽하게 잠금을 한 것 같지만 컴퓨터에서는 타임 슬라이스 아웃 때문에
문제가 발생할 수 있다.
타임 슬라이스 아웃
[왜 잠금이 뚫릴까?]
"세마포어가 있는데 왜 굳이 '모니터'가 등장했을까?"
wait()를 나올 때 post()를 호출하면 된다.[왜 모니터가 필요할까?]
순서 오류 (post() -> wait())
wait()로 자원을 획득하고, post()로 반납해야 하는데누락 오류 (wait() 또는 post() 생략)
wait()을 빼먹으면post()를 빼먹으면 세마포어 카운트가 고갈되어중복 사용
wait()를 호출하면이런 휴먼 에러를 막기 위해 등장한 것이 모니터이다.
모니터는 캡슐 같습니다. 개발자가 일일이 잠금을 걸고 푸는 게 아니라
스레드가 모니터에게 "나 이거 할게"라고 요청만 하면
모니터가 내부적으로 알아서 줄을 세우고(Queue) 동기화를 처리해 준다.
자바(Java) 언어의 synchronized 키워드가 바로
이 모니터를 구현한 가장 대표적인 예시이다.
"우선순위 역전: 대장 스레드가 부하 스레드 때문에 멈춘다고?"
[왜 역전될까?]
가장 낮은 스레드(T9)가 먼저 임계구역에 들어가 열쇠를 쥐고 있다.
가장 높은 우선순위 스레드(T1)가 임계구역에 들어가려 하지만
T9가 열쇠를 쥐고 있으니 대기한다.
이때 임계구역을 쓰지 않는 중간 우선순위 스레드(T5)가 등장한다.
T5는 T9보다 우선순위가 높으니 CPU를 빼앗아 먼저 실행된다.
결과적으로: T9는 CPU를 뺏겨 열쇠를 반납하지 못하고,
T1은 T9가 열쇠를 반납하지 않아 무한정 기다리게 된다.
가장 높은 T1이 가장 늦게 실행되는 모순(우선순위 역전)이 발생한 것이다.
[어떻게 해결할까? - 우선순위 상속]