ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 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
Designed by Tistory.