Java Adv 10 - wait, notify

Java MultiTherad

자바는 처음부터 멀티스레드를 고려하며 탄생한 언어다.
synchronized 를 사용한 임계 영역 안에서 락을 가지고 무한 대기하는 문제는 흥미롭게도 Object 클래스에 해결 방안이 있다. Object 클래스는 이런 문제를 해결할 수 있는 wait() , notify() 라는 메서드를 제공한다. Object 는 모든 자바 객체의 부모이기 때문에, 여기 있는 기능들은 모두 자바 언어의 기본 기능이라 생각하면 된다.

wait(), notify()

image

  • Object.wait()
    • 현재 스레드가 가진 락을 반납하고 대기(WAITING )한다.
    • 현재 스레드를 대기(WAITING) 상태로 전환한다.
    • 이 메서드는 현재 스레드가 synchronized 블록이나메서드에서 락을 소유하고 있을 때만 호출할 수 있다.
    • 호출한 스레드는 락을 반납하고, 다른 스레드가 해당 락을 획득할 수 있도록 한다.
    • 이렇게 대기 상태로 전환된 스레드는 다른 스레드가 notify() 또는notifyAll() 을 호출할 때까지 대기 상태를 유지한다.
  • Object.notify()
    • 대기 중인 스레드 중 하나를 깨운다. 이 메서드는 synchronized 블록이나 메서드에서 호출되어야 한다.
    • 깨운 스레드는 락을 다시 획득할 기회를 얻게 된다. 만약 대기 중인 스레드가 여러 개라면, 그 중 하나만이 깨워지게 된다.
  • Object.notifyAll()
    • 대기 중인 모든 스레드를 깨운다.
    • 이 메서드 역시 synchronized 블록이나 메서드에서 호출되어야 하며, 모든 대기 중인 스레드가 락을 획득할 수 있는 기회를 얻게 된다.
    • 이 방법은 모든 스레드를 깨워야 할 필요가 있는 경우에 유용하다.

달라진 점은?

앞서 작성한 코드는 sleep() 코드는 제거하고 대신에 Object.wait() 를 사용하자. Object 는 모든 클래스의 부모이므로 자바의 모든 객체는 해당 기능을 사용할 수 있다. synchronized 를 통해 임계 영역을 설정한다. 생산자 스레드는 락 획득을 시도한다. 락을 획득한 생산자 스레드는 반복문을 사용해서 큐에 빈 공간이 생기는지 주기적으로 체크한다. 만약 빈 공간이 없다면 Object.wait() 을 사용해서 대기한다. 참고로 **대기할 락을 반납하고 대기**한다. 그리고 대기 상태에서 깨어나면, 다시 반복문에서 큐의 빈 공간을 체크한다. wait() 를 호출해서 대기하는 경우 RUNNABLE WAITING 상태가 된다. 생산자가 데이터를 큐에 저장하고 나면 notify() 를 통해 저장된 데이터가 있다고 대기하는 스레드에 알려주어야 한다. 예를 들어서 큐에 데이터가 없어서 대기하는 소비자 스레드가 있다고 가정하자. 이때 notify() 를 호출하면 소비자 스레드는 깨어나서 저장된 데이터를 획득할 수 있다.

wait, notify의 한계

Object.wait(), Object.notify() 방식은 스레드 대기 집학 하나에 생산자, 소비자 스레드를 모두 관리한다. 그리고 notify() 를 호출할 때 임의의 스레드가 선택된다. 따라서 앞서 살펴본 것 처럼 큐에 데이터가 없는 상황에 소비자가 같은 소비자를 깨우는 비효율이 발생할 수 있다. 또는 큐에 데이터가 가득 차있는데 생산자가 같은 생산자를 깨우는 비효율도 발생할 수 있다. 같은 종류의 스레드를 깨울 때 비효율이 발생하고 있다. 만약 생산자가 소비자를 깨우고, 소비자가 생산자를 깨운다면 비효율이 발생하지 않는다.

Reference

김영한님의 자바 강의

https://stackoverflow.com/questions/50001353/java-one-producer-and-two-consumers

https://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html



© 2022. by taewoo

Powered by taewoo