[JAVA] wait(), notify(), notifyAll() 메서드

wait()

  • wait() 메서드는 스레드를 대기 상태로 전환시키고 다른 스레드가 notify() 또는 notifyAll() 메서드를 호출할 때까지 기다립니다.
  • wait() 메서드를 호출하면 해당 객체의 락을 반납하고 스레드는 일시적으로 대기 상태가 됩니다.

notify()

  • notify() 메서드는 대기 중인 스레드 중 하나를 깨웁니다.
  • 호출 시 대기 중인 스레드 중 하나가 깨어나서 실행 대기 상태로 전환됩니다.

notifyAll()

  • notifyAll() 메서드는 대기 중인 모든 스레드를 깨웁니다.
  • 호출 시 모든 대기 중인 스레드가 실행 대기 상태로 전환됩니다.

주의사항

  • 이 메서드들은 반드시 동기화된 블록 내에서 호출되어야 합니다. 그렇지 않으면 IllegalMonitorStateException이 발생합니다.
  • wait() 메서드는 항상 while 루프 안에서 호출되어야 합니다. 이는 spurious wakeups에 대비하여 스레드가 깨어난 후에도 조건을 다시 확인할 수 있게 합니다
  • 스레드가 wait() 상태일 때 인터럽트가 발생하면 InterruptedException이 발생하므로 이에 대한 처리가 필요합니다.

예시

은행에서 신용카드회사가 A원 출금 요청을 걸어놓으면 사용자가 입금을 지속적으로 하는데 A원이 되면 인출되는 시스템으로 해봤습니다.

public class Bank {
	private int MONEY = 0;
	final private int MAX_MONEY = 20000;
	final private int MIN_MONEY = 0;
	
	public void withdraw(String threadName, int money) {
		System.out.println(threadName + "님 " + money + "원 출금 요청");
		synchronized(this) {
			while(MONEY - money < MIN_MONEY) {
				try {
					System.out.println(threadName + "님 잠듬");
					wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			this.MONEY -= money;
			notifyAll();
			System.out.println(threadName + "님 " + money + "원 출금");
			System.out.println("잔액: " + this.MONEY + "원");
		}		
	}
	
	public void deposit(String threadName, int money) {
		System.out.println(threadName + "님 " + money + "원 입금 요청");
		synchronized(this) {
			while(MONEY + money > MAX_MONEY) {
				try {
					System.out.println(threadName + "님 잠듬");
					wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			this.MONEY += money;
			notifyAll();
			System.out.println(threadName + "님 " + money + "원 입금");
			System.out.println("잔액: " + this.MONEY + "원");
		}
	}
}
public class Main {
	public static void main(String[] args) {
		Bank bank = new Bank();
		Thread threadA = new Thread(() -> {
			for(int i = 0; i< 5; i++) {
				bank.deposit("사용자", 2000);
			}
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			bank.deposit("사용자", 10000);
		});
		
		Thread threadB = new Thread(() -> {
			bank.withdraw("신용카드", 20000);			
		});
		
		threadA.start();
		threadB.start();
	}
}

신용카드 회사가 20000원을 인출 요청 걸어 놓으면 그 금액이 안되면 wait로 빠지는 코드입니다. 사용자가 notify로 깨워도 신용카드 회사는 그 금액이 안되기 때문에 wait에 머뭅니다. 사용자가 입금을 지속적으로 해서 금액이 20000원이 되면 신용카드 회사는 20000을 출금해 갑니다.

신용카드님 20000원 출금 요청
신용카드님 wait
사용자님 2000원 입금 요청
사용자님 2000원 입금
잔액: 2000원
사용자님 2000원 입금 요청
신용카드님 wait
사용자님 2000원 입금
잔액: 4000원
사용자님 2000원 입금 요청
신용카드님 wait
사용자님 2000원 입금
잔액: 6000원
사용자님 2000원 입금 요청
사용자님 2000원 입금
잔액: 8000원
사용자님 2000원 입금 요청
사용자님 2000원 입금
잔액: 10000원
신용카드님 wait
사용자님 10000원 입금 요청
사용자님 10000원 입금
잔액: 20000원
신용카드님 20000원 출금
잔액: 0원