Mutex: 동기화 문제를 해결하기 위한 가장 간단한 방법
semaphore: 좀더 강력하고 편리하며 효율적인 툴
Monitor: 위의 두가지가 가지는 단점을 극복한 도구
Liveness: Progress 달성을 보장해줌
Mutex = mutual exclusion
임계영역을 보호하고 레이스 컨디션을 방지한다. 프로세스는 임계영역에 진입하기 위해 반드시 lock을 획득하고, exit할 때에는 lock을 반납해야한다.

두 개의 함수 acquire()와 release()를 제공해주면 구현할 수 있다. available는 불리언 변수다. 단 두 함수는 atomically하게 작동해야한다. 이 둘은 compare and swap을 통해 구현할 수 있다.

Busy waiting문제: 어떤 프로세스가 임계영역에 들어가기 위해 acquire 내에서 루프 반복이 실행된다. 이는 단일 코어 멀티 프로그래밍 환경에서 문제가 된다. 단일 CPU 사이클을 루프 반복에 소모하고 있어서 효율성이 떨어진다.
Spinlock busy waiting을 사용하는 mutex을 spinlock이라고 부른다. 위에서 말한 단점이 있지만, 장점이 없는것도 아니다. CPU 코어가 다수인 경우에는 context switch가 필요 없을 수 있기 때문에 이 부분에서 시간을 절약할 수 있다.
semaphore: 신호장치, 신호기
Semaphore S는 정수로서 두 개의 atomic operation에 의해 접근된다. wait), signal(), 혹은 P(), V()라고 쓴다.

마찬가지로 위 두 함수도 atomically 하게 작동해야한다.
Binary Semaphore
이건 사실상 mutex lock과 동일하다.
Counting Semaphore
이 경우 유한개의 인스턴스에 대해 적용될 수 있다.
가용 자원의 수 만큼 semaphore S를 초기화 해주고, 자원을 사용할 때마다, wait()을 동해 decrement를 수행하고 자원 할당이 해제될 때마다, signal()을 통해 increment를 수행해주면 된다. S가 0일 때에는 임계영역에 접근하려는 프로세스들은 block될 것이다. 이제 이걸 가지고 동기화 문제를 해결해보자.
두 개의 프로세스가 실행되는 경우. synch를 0으로 초기화 하여 다음과 같이 설계하면 된다.

하지만 semaphore도 busy waitng 문제가 발생한다. 이 문제를 해결하기 위해 우리는 P(), V()를 수정해야한다.
프로세스가 wait()으로 루프를 돌 때, waiting queue로 보내지도록 하면된다. 그리고 다른 프로세스가 signal()을 실행하면 대기중이던 프로세스는 ready queue로 위치시켜 재시작을 시키면 된다.

semaphore가 편리하고 효과적이긴 하나 timing error로 인해 사용하기 어렵다.
예를 들어, binary semaphore를 모든 프로세스가 사용하는 경우. wait()과 signal()순서를 지키지 않으면 두 개의 프로세스가 임계영역에 동시에 진입하게 된다.
[상황1]

사실 굉장히 어이없는 실수이긴 하지만, 이런 상황이 잘 발생한다. 이를 해결하기 위해 좀더 쉬운 monitor type을 사용한다.
Monitor type이란 mutex를 제공해주는 프로그래머 정의 연산 집합을 포함한 ADT다.


Conditional Variables
모니터가 자체적으로는 동기화 문제를 풀기 어려워서 필요로 하는 것이다.


자바는 monitor와 비슷하게 작동한다.(monitor-lock, intrinsic-lock)
syncronized
임계영역에 해당하는 코드 블록을 선언할 떄 사용하는 자바 키워드. 해당 코드 블록에는 모니터락을 획득해야 진입 가능하다. 모니터락을 가진 객체 인스턴스를 지정할 수 있다. 메소드에 선언하면 메소드 코드 블록 전체가 임계영역으로 지정된다. 이 때, 모니터락을 가진 객체 인스턴스는 this 객체 인스턴스임

wait(), notify() 메소드
java.lang.Object 클래스에 선언됨: 모든 자바 객체가 가진 메소드다.
스레드가 어떤 객체의 wait() 메소드를 호출하면, 해당 객체의 모니터락을 획득하기 위해 대기 상태로 진입한다. 스레드가 어떤 객체의 notify() 메소드를 호출하면, 해당 객체 모니터에 대기중인 스레드 '하나'를 꺠운다. 만약 notifyAll() 메소드를 호출하면. 해당 객체 모니터에 대기중인 스레드 전부를 깨운다.
Java 구현


progress와 bounded-waiting을 Semaphore와 monitor는 해결해주지 못한다.
Liveness는 이 문제를 해결해준다.
두 가지 상황에서 liveness는 실패할 수 있다.
[상황1] Deadlock

열쇠 두개가 필요한데 서로 한개씩 가져가고, 상대방에게 요구하는 상황...
[상황2] Priority Inversion <- priority-inheritance로 해결 가능.