-
wait(), notify()Java/Java 문법 종합반 강의 정리 2024. 1. 3. 19:57
침범을 막은 코드(synchronized)를 수행하다가 작업을 더 이상 진행할 상황이 아니면,
wait()을 호출하여 쓰레드가 Lock을 반납하고 기다리게 할 수 있다.
- 그럼 다른 쓰레드가 락을 얻어 해당 객체에 대한 작업을 수행할 수 있게 되고,
- 추후에 작업을 진행할 수 있는 상황이 되면 notify()를 호출해서,
- 작업을 중단했던 쓰레드가 다시 Lock을 얻어 진행할 수 있게 된다.
1. wait() : 실행 중이던 쓰레드는 해당 객체의 대기실(waiting pool)에서 통지(notify)를 기다린다
2. notify() : 해당 객체의 대기실(waiting pool)에 있는 모든 쓰레드 중에서 임의의 쓰레드만 통지를 받는다.
예제를 통해 두 메서드의 활용법을 알아보자
public class Main { public static String[] itemList = { "MacBook", "IPhone", "AirPods", "iMac", "Mac mini" }; public static AppleStore appleStore = new AppleStore(); public static final int MAX_ITEM = 5; public static void main(String[] args) { // 가게 점원 Runnable StoreClerk = () -> { while (true) { int randomItem = (int) (Math.random() * MAX_ITEM); appleStore.restock(itemList[randomItem]); try { Thread.sleep(50); } catch (InterruptedException ignored) { } } }; // 고객 Runnable Customer = () -> { while (true) { try { Thread.sleep(77); } catch (InterruptedException ignored) { } int randomItem = (int) (Math.random() * MAX_ITEM); appleStore.sale(itemList[randomItem]); System.out.println(Thread.currentThread().getName() + " Purchase Item " + itemList[randomItem]); } }; new Thread(StoreClerk, "StoreClerk").start(); new Thread(Customer, "Customer1").start(); new Thread(Customer, "Customer2").start(); } } class AppleStore { private List<String> inventory = new ArrayList<>(); public void restock(String item) { synchronized (this) { while (inventory.size() >= Main.MAX_ITEM) { System.out.println(Thread.currentThread().getName() + " Waiting!"); try { wait(); // 재고가 꽉 차있어서 재입고하지 않고 기다리는 중! Thread.sleep(333); } catch (InterruptedException e) { e.printStackTrace(); } } // 재입고 inventory.add(item); notify(); // 재입고 되었음을 고객에게 알려주기 System.out.println("Inventory 현황: " + inventory.toString()); } } public synchronized void sale(String itemName) { while (inventory.size() == 0) { System.out.println(Thread.currentThread().getName() + " Waiting!"); try { wait(); // 재고가 없기 때문에 고객 대기중 Thread.sleep(333); } catch (InterruptedException e) { e.printStackTrace(); } } while (true) { // 고객이 주문한 제품이 있는지 확인 for (int i = 0; i < inventory.size(); i++) { if (itemName.equals(inventory.get(i))) { inventory.remove(itemName); notify(); // 제품 하나 팔렸으니 재입고 하라고 알려주기 return; // 메서드 종료 } } // 고객이 찾는 제품이 없을 경우 try { System.out.println(Thread.currentThread().getName() + " Waiting!"); wait(); Thread.sleep(333); } catch (InterruptedException e) { e.printStackTrace(); } } } }
점원은 상품이 최대치인 5개가 다 찼을 때 wait 를 하고, 고객이 상품을 사갔을 때 notify 를 받는다.
고객은 상품이 0개 이거나, 내가 원하는 상품이 없을 때 wait 하고, 점원이 상품을 재입고 할 때 notify를 받는다.
만약 코드를 자세히 살펴봤다면 이상함을 눈치 챌 수 있을 것이다.
while(true), 따로 종료조건도 없기 때문에 쓰레드를 생성하고 실행 했으나 종료할 탈출구를 만들어 주지 않았다.
그 결과 랜덤하게 상호작용을 하다 보면 끝에 이런 상황이 나온다.
고객과 점원이 둘다 Wating을 하고 있다.
만약 점원이 항상 5가지의 물건 중 없는 종류만 추가했다면 무한히 이어졌을 것이다.
하지만 그런 로직은 없었기 때문에 inventory.size() == 5 임에도 고객이 원하는 물건이 없는 현상을 맞딱드린 것이다.
이런 것을 '병목 현상' 이라고 한다.
이런것을 해결하기 위해 wait, notify 를 할 때는 정확한 대상을 지정해야 할 필요성이 있다.
그 방법을 다음에 알아볼 Lock, Condition 을 통해 해결할 수 있다.
'Java > Java 문법 종합반 강의 정리' 카테고리의 다른 글
Java8 에서의 변경점 (1) 2024.01.09 Lock, Conditon (6) 2024.01.03 join, yield, synchronized (4) 2024.01.03 쓰레드의 상태와 제어 (2) 2024.01.03 데몬 쓰레드, 쓰레드 우선순위, 쓰레드 그룹 (0) 2024.01.03