synchronized와 ReentrantLock의 대기상태에 대해 알아보겠습니다
synchornized의 대기상태는 두가지 경우가 있습니다
먼저 BLOCKED 상태로 락 획득을 대기하고 synchornized를 시작할 때 락이 없으면 대기합니다
다른 스레드가 synchronized를 빠져나갈 때 대기가 풀리며 락 획득을 시도합니다
또한 WAITING 상태로 대기하는 경우도 이습니다
wait()를 호출했을 때 스레드 대기 잡합에서 대기하고 다른 스레드가 notify()를 호출했을 때
빠져나갑니다
락을 기다리는 스레드가 여러개라면 그 스레드에 대해서 어디선가 관리되고 있어야 합니다
사실 락 획득을 대기하기 위해 BLOCKED되는 경우 락 대기 집합이라는 곳에서 대기하고 있습니다
스레드 대기 집합에 있는 특정 스레드가 큐에서 데이터를 확인하기 위해 락을 획득하고
다른 스레드들은 락 대기 집합에서 락을 획득할 떄까지 대기합니다
큐에 데이터가 없으면 락을 반납하고 다시 획득하고를 반복하며, 다시 스레드 대기 집합으로 돌아가고
생산자 스레드에 의해 데이터가 채워지면 위 과정중 한 스레드가 작업을 완료합니다
자바의 모든 객체 인스턴스는 멀티스레드와 임계영역을 다루기 위해
내부에 3가지 기본 요소를 가집니다
각 요소는 서로 맞물려서 돌아갑니다
synchronized를 사용한 임계영역에 들어가려면 모니터락이 필요하며
모니터 락이 없으면 락 대기 집합에 들어가서 BLOCKED 상태로 락을 기다립니다
모니터 락을 반납하면 락 대기 집합에 있는 스레드 중 하나가 락을 획득하고 BLOCKED -> RUNNABLE 상태가 됩니다
마찬가지로 wait()을 호출해서 스레드 대기 집합에 들어가기 위해서는 모니터락이 필요합니다
스레드 대기 집합에 들어가면 모니터락을 반납하며
스레드가 notify()를 호출하면 스레드 대기 집합에 있는 스레드 중 하나가 스레드 대기 집합을 빠져나옵니다
그리고 모니터 락 획득을 시도하며 획득하면 임계영역을 수행하고
획득하지 못하면 락 대기 집합에 들어가서 BLOCKED 상태로 락을 기다립니다
ReentrantLock도 마찬가지로 2가지 단계의 상태가 존재합니다
이번에는 ReentrantLock의 대기 큐에서 관리합니다
WAITING 상태로 락 획득을 대기하며, lock.lock()을 호출했을 때 락이 없으면 대기합니다
다른 스레드가 lock.unlock()을 호출했을 때 대기가 풀리며 락 획득을 시도하고
락을 획득하면 대기 큐를 빠져나갑니다
Condition.await()을 호출했을 때, condition 객체의 스레드 대기 공간에서 관리하며
WAITING 상태로 대기하고 다른 스레드가 condition.signal()을 호출했을 때
condition 객체의 스레드 대기 공간에서 빠져나갑니다
참고로 ReentrantLock도 2단계 대기소가 되어있습니다
condition 객체의 스레드 대기 공간을 빠져나온다고 바로 실행되는 것은 아니며
임계영역 안에서 항상 락이 있는 하나의 스레드만 실행될 수 있습니다
ReentrantLock의 락을 획득해야 RUNNABLE 상태가 되며 그 다음 코드를 실행할 수 있고,
락을 획득하지 못하면 WAITING 상태로 락을 획득할 때까지 ReentrantLock의 대기 큐에 대기합니다