Back to posts

모니터 (wait, signal, broadcast)

모니터의 개념과 wait/notify/notifyAll을 간단히 정리.

2024년 12월 29일

참고자료

두 번째 영상은 정말 좋다. 구독을 누를 수밖에 없는 퀄리티다. 비슷한 내용은 대부분 영상 정보를 활용했다. 시간이 없다면 영상만 봐도 좋다.

사전에 알아두면 좋은 링크:

모니터?

모니터는 언제 필요한가

  • 한 번에 하나의 쓰레드만 실행되어야 할 때
  • 여러 쓰레드가 협업해야 할 때

즉 동시성에 대한 이야기다.

모니터의 구성요소

  • mutex: 크리티컬 섹션에서 mutual exclusion을 보장
    • mutex lock을 취득하지 못한 쓰레드는 큐에서 waiting 상태로 대기
    • lock을 반환하면 대기 중이던 쓰레드 중 하나가 실행
  • condition variable (cv)
    • waiting queue(wq)를 가진다
    • 조건이 충족되길 기다리는 쓰레드들이 대기

자바는 synchronized와 함께 모니터와 락을 사용한다.

cv의 주요 동작

  • wait: 자기 자신을 wq에 넣고 대기
  • signal: wq의 대기 쓰레드 중 하나를 깨움
  • broadcast: 전부 깨움

모니터와 크리티컬 섹션

모니터와 크리티컬 섹션의 흐름은 아래와 같다.

출처 : https://www.youtube.com/watch?v=Dms1oBmRAlo

사용되는 두 개의 큐:

  • entry queue: 크리티컬 섹션 진입 대기
  • waiting queue: 조건 충족 대기 (cv가 관리)

Java 모니터 특징

  • 모든 객체는 모니터를 갖는다.
  • 모니터의 3가지 동작은 모두 해당 객체의 cv에 대한 동작이다.
    • wait
    • notify (signal)
    • notifyAll (broadcast)
  • 모니터에는 mutex lock이 있다.
  • mutual exclusion은 synchronized로 해결한다.
  • cv는 객체당 1개만 가진다고 간주한다.

notify는 대기 중인 쓰레드 중 랜덤 1개를 깨운다. notifyAll은 대기 중인 쓰레드를 모두 깨운다.

대체로 notifyAll이 더 실용적이라고 한다.

But in most cases, it would be more viable to implement notifyAll(). https://www.baeldung.com/java-wait-notify#1-notify

생산자-소비자 문제

출처 : https://www.youtube.com/watch?v=Dms1oBmRAlo

특이사항:

  1. while을 쓰는 이유는 OS에 따라 wait에서 잘못 깨우는 경우가 있기 때문이다. 깨어난 뒤 조건을 다시 확인해야 해서 if가 아니라 while을 쓴다.
  2. 자바 객체의 모니터는 condition variable이 1개뿐이다. 특정 쓰레드만 깨우기 어렵다. 그래서 notify보다 notifyAll을 쓰는 경우가 많다. 다만 notifyAll 후 다시 entry queue로 들어가는 비효율이 있을 수 있다.

자바에서 조건에 맞는 쓰레드만 깨우려면 java.util.concurrent의 ReentrantLock + Condition을 쓰면 된다.

결론

동시성을 위해 모니터 개념을 이해했다. 모니터는 자바에만 있는 개념이 아니며, wait, signal, broadcast로 쓰레드 협업을 만든다. 자바는 wait, notify, notifyAll로 대응된다.

쓰레드는 OS에 따라 비결정적이라 저수준 구현은 어렵다. 가독성/편의성/기능 측면에서 1.5의 동시성 라이브러리(java.util.concurrent)를 쓰는 게 좋다. 그래도 저수준 개념을 이해하는 건 동시성 라이브러리를 깊게 이해하는 데 도움이 된다.