wait( ) / notify( ) 메서드
wait( ) / notify( ) 메서드를 활용한 동기화 프로그래밍
- 리소스가 어떤 조건에서 더 이상 유효하지 않은 경우 리소스를 기다리기 위해 Thread 가 wait() 상태가 된다.
- wait() 상태가 된 Thread은 notify()가 호출될 때까지 기다린다.
- 유효한 자원이 생기면 notify()가 호출되고 wait() 하고 있는 Thread 중 무작위로 하나의 Thread를 재시작하도록 한다.
- 오래 기다렸거나, 우선순위 높은 Thread가 먼저 재시작되는 것이 아닌 무작위 - 영원히 선택되지 못하는 Thread 있을 가능성 존재 - notifyAll() 호출 권장
- notifyAll()이 호출되는 경우 wait() 하고 있는 모든 Thread가 재시작 된다.
- 이 경우 유효한 리소스만큼의 Thread만이 수행될 수 있고 자원을 갖지 못한 Thread의 경우는 다시 wait() 상태로 만든다.
- notifyAll()로 모든 Thread 재시작되면 일종의 경쟁상태가 된다. - 하나만 깨워서 수행하는 것보다 모두 다 깨운 뒤 레이스 컨디션으로 경쟁 상태가 되는 것이 더 공정하다.
- 자바에서는 notifyAll() 메서드의 사용을 권장한다.
도서관에서 책을 빌리는 예제
책은 한정되어있고, 학생은 많은 경우
- 책이 들어올 때까지 앞에서 기다리는 경우 -> 책이 들어왔나요? 들어왔나요? 리소스 계속 체크 - 오버헤드
- 책이 들어오면 연락 줄게요 -> notify() / norifyAll()
1. 책 들어오면 1명에게만 연락을 주는 것이 notify() -
2. 모든 대기자에게 연락 주는 것이 notifyAll() -
1. 도서관에 책 6권이 있고 / 학생 5명이 한 권씩 빌리고 반납하는 상황
Fast도서관에 책 추가
책 빌려갈 때 - 반납할 때 메서드
학생 Thread 생성
출력 Test -
학생 객체 잘 만들어지고, Thread start 잘됨을 확인
lend 빌리고 5초 뒤에 return 잘됨을 확인
로그는 순서대로 안되어있지만 순서대로 빌려가고 반납하고 한 것이다.
2. 도서관에 책이 3권이 있고 / 학생 5명이 책을 빌리려고 하는 경우
도서관 책 3권으로 수정 후 실행 Test
학생 3명은 1권씩 빌려가서 반납했지만 나머지 2명에게는 OutOfBoundException
없는데 빌리려고 해서 그런다 - 빌리기 전에 조건 추가
공유 리소스에 synchronized 해주고 / 빌릴 책 없으면 null; 반환되도록 if - else로 변경
Student Thread에 빌리려고 했을 때 반환 값 null 이면 " 빌리지 못했음 " 출력되도록 해준다.
출력 test
3명 빌리고 2명 빌리지 못하고 / 3명 반납한 것 확인
synchronized 해줘서 exception 상황 발생 안 하고 실행됨을 확인
하지만 - 학생 2명은 빌리지 못했다는 포기상태가 된다.
이게 아니라 기다리고 있으면 책이 오면 알려드릴게요 - wait
wait 걸어주는 상황은 도서관에 빌릴 책이 없을 때
wait이 끝나는 시점에 notify() 나 notifyAll() 메서드로 Thread 깨워준다.
notify()를 사용하는 경우

빌릴 책이 없는 2명 waiting 스타트
책이 생기면 무작위로 1개의 thread - 학생 깨어나서 빌린다.
다시 책이 생기면 무작위 1개 Thread - 학생 깨어나서 빌린다.
다 빌리고 읽고 반납하고 끝
notifyAll()을 사용하는 경우
lendBook()/ returnBook() 메서드 수정
all로 깨웠을 때 잘 반납되고 빌리고 해서 잘 될 수 있겠지만, 빌리지 못하는 경우 발생 가능성 있음
깨어났다가 차지 못하면 다시 waiting 들어가도록 변경 필요
학생 1이 책 반납해서 책 1권 생김
학생 3과 학생 2 깨어나서 일종의 경쟁상태
학생 3이 빌려감. 학생 2는 깨어났지만 책을 못 빌려서 다시 wait상태로 스타트 - 빌리지 못했음 포기 상태 X
if -> while로 변경해줌
책 1권 반납되어서 자고 있던 Thread 2개 모두 all 깨어났는데
학생 3이 빌려가고 책이 없다 -> notify() 일 때는 한 개의 Thread 만을 깨워서 빌려주고 해서 if - notify() 괜찮았지만
notifyAll()에서는 모든 Thread들을 깨운 뒤 일종의 경쟁상태 - 배정받은 thread는 빌려가고 밀린 Thread는 다시 wait상태로 돌아가도록 하나하나 한 번의 if가 아닌 책 빌릴 때까지 반복적으로 wating 하도록 while로 변경 - 기다리는 상태 유지시켜준다.
review
wait과 notify() / notifyAll()에 대해 실습해보았다.
wait() - 포기해서 Thread 끝내지 말고 기다려 사용 가능할 때 깨워줄게
notify() - 자리 하나 났다. 아무나 하나만 일어나세요
notifyAll() - 자리 하나 났다. 모두 일어나세요.
공유 리소스나 한정된 자원을 여러 Thread가 서로 사용하려고 하는 경우
첫 경쟁에 밀려 cpu를 차지하지 못한 Thread들은 차지하지 못했음 하고, 끝내버리는 것이 아닌
NotRunnable 상태로 대기하다가 공유 리소스나 자원을 사용할 수 있는 상태가 되면
notify() / notifyAll()로 호출해주면 Runnable상태로 돌아와서 배정받거나 경쟁하거나 하도록 해준다!
실습해보고 중요하다고 생각한 부분은
wait과 notify / notifyAll을 걸어주는 부분이 어딘지 체크하는 부분이라고 생각했다.
엉뚱한 곳에 메서드를 사용하면 원하는 결과나 효과를 얻을 수 없을 것이다.
무작위로 하나씩 깨우는 notify()는 영원히 호출 안 되는 Thread 있을 수 있어서
notifyAll()로 다 깨운 뒤 경쟁하는 것이 더 공정하다고 생각해서 java에서는 All을 권장한다고 하는데
경쟁에서도 계속 밀리는 Thread가 있어서 계속 수행 안되면 똑같은 거 아닌가?
경쟁에서 진거는 process의 책임이 아니라 Thread의 책임이라고 생각해서 그러는 건가?
아니면 경쟁 많이 밀린 Thread 우선으로 처리해주는 부분을 추가해 줄 수 있는 건가 생각이 들었다.