JAVA3_09_wait(), notify()

charl hi·2021년 10월 3일
0

JAVA3

목록 보기
9/9

동기화의 단점

  • 데이터 보호는 되나, 효율이 떨어진다.
    -> 동기화의 효율을 높이기 위해 wait(), notify() 사용


wait(), notify()

  • Object클래스에 정의되어 있으며, ✨✨동기화 블록 내에서만 사용 가능

wait()

  • 객체의 lock을 풀고 쓰레드를 해당 객체의 waiting pool에 넣는다.


notify()

  • waiting pool에 대기중인 쓰레드 중 하나를 깨운다.

notifyAll()

  • waiting pool에 대기중인 모든 쓰레드를 깨운다.

  • balance < money일 때 돈을 출금할 수 없으므로 돈이 입금될 때 까지 대기탄다. (while문을 계속 돈다)

  • 즉 deposit()을 통해 balance >= money될 때까지 wait() 호출로 lock을 반납하고 waiting pool에서 대기탄다.

  • ✨왜 lock 반납? : 객체1개엔 쓰레드1개만 동기화 가능하니까!!

  • balance >= money되면 while문을 빠져나와 출금(balance -= money;)한다.



Ex13_13

  • table.add : 요리사는 Table에 음식을 추가
  • eatFood() : 손님은 Table의 음식을 소비
  • table.remove(food) : 테이블에서 음식을 제거
  • ✨✨요리사와 손님이 같은 객체Table을 공유하므로 동기화가 필요

1. 동기화는 어떻게?

  • 예외 발생 : Table을 여러 쓰레드가 공유하기 때문에 작업 중에 끼어들기 발생
    ->✨ Table의 add()와 remove()를 synchronized로 동기화

2. 효율성 제고

  • 비효율 : 손님CUST2이 Table에 lock건 상태를 지속해서, 요리사가 Table의 lock을 얻을 수 없어서 음식을 추가하지 못함
    -> ✨음식이 없을 때, wait()으로 손님이 lock을 풀고 기다리게 한다.
    -> ✨요리사가 음식을 추가하면, notify()로 손님에게 알린다. 손님이 lock을 재획득

import java.util.ArrayList;

public class Ex13_13 {

	public static void main(String[] args) throws Exception{
		Table table = new Table();
		
		Cook c = new Cook(table);
		Thread th1 = new Thread(c, "COOK");
		th1.start();	//이 세줄을 줄인 게 아래 방식***
		
		new Thread(new Customer(table, "donut"), "CUST1").start();
		new Thread(new Customer(table, "burger"), "CUST2").start();
		
		Thread.sleep(2000);	//**이걸 사용하려면 위에 throws Exception 해야한다.ㅠㅠ??
		System.exit(0);
	}

}

class Customer implements Runnable{
	private Table table;
	private String food;
	
	Customer(Table table, String food){
		this.table = table;
		this.food = food;
	}
	
	public void run() {
		while(true) {
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {}
			String name = Thread.currentThread().getName();
			
			table.remove(food);
			System.out.println(name+" ate a "+food);
		}
	}
}

class Cook implements Runnable{
	private Table table;
	
	Cook(Table table) {
		this.table = table;
	}
	
	public void run() {
		while(true) {
			//0<= x <3 -> 0,1,2
			int idx = (int)(Math.random() * table.dishNum());
			table.add(table.dishNames[idx]);
			//**메뉴판 0, 1:donut, 2:burger 을 추가
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {}
		}
	}
	
	
}

class Table{
	String[] dishNames = {"donut", "donut", "burger"};	//donut확률 높이기
	final int MAX_FOOD = 6;
	private ArrayList<String> dishes = new ArrayList<>();

	//음식 추가
	public synchronized void add(String dish) {
		//***음식이 꽉 찼을 때는
		while(dishes.size() >= MAX_FOOD) {
			String name = Thread.currentThread().getName();
			System.out.println(name+" is waiting.");
			try {
				wait(); //COOK 쓰레드 기다리게
				//**음식이 꽉 찼을 땐 요리사 기다리게
			} catch (InterruptedException e) {}
		}
		//음식 다 안찼을때는 항상 음식 추가
		dishes.add(dish);
		notify();
		//**음식이 추가되면 기다리고 있는 손님 깨우기
		System.out.println("Dishes: "+dishes.toString());
	}//synchronized add
	
	//손님이 음식 먹기
	public void remove(String dishName) {
		synchronized (this) {
			String name = Thread.currentThread().getName();
			
			//***음식이 없으면
			while(dishes.size()==0) {
				System.out.println(name + " is waiting.");
				try {
					wait(); //CUST 쓰레드 기다리게
					//**음식이 없으면 손님 기다리게
					Thread.sleep(500);
				} catch (InterruptedException e) {}
			}
			
			//***원하는 음식이 있을 때까지 dishes 뒤지기
			while(true) {
				for(int i=0; i<dishes.size(); i++) {
					//***원하는 음식이 있으면
					if(dishName.equals(dishes.get(i))) {
						dishes.remove(i);
						notify(); //기다리고 있는 COOK 깨우기
						//**손님이 음식 먹으면, 기다리고 있는 요리사 깨우기
						return;
					}
				}//for
				
				//***원하는 음식이 없으면
				try {
					System.out.println(name + " is waiting.");
					wait(); //CUST 쓰레드 기다리게
					//**원하는 음식이 없으면 손님 기다리게
					Thread.sleep(500);
				} catch (InterruptedException e) {}
			}//while
		}//synchronized
	}//remove
	public int dishNum() {return dishNames.length;}
}

Dishes: [donut]
Dishes: [donut, burger]
Dishes: [donut, burger, burger]
Dishes: [donut, burger, burger, donut]
Dishes: [donut, burger, burger, donut, donut]
Dishes: [donut, burger, burger, donut, donut, burger]
COOK is waiting.
CUST1 ate a donut
Dishes: [burger, donut, donut, burger, donut]
CUST2 ate a burger
Dishes: [burger, donut, donut, burger, donut, burger]
COOK is waiting.
CUST1 ate a donut
Dishes: [burger, donut, burger, donut, burger, burger]
CUST2 ate a burger
Dishes: [donut, burger, donut, burger, burger, donut]
COOK is waiting.
CUST1 ate a donut
Dishes: [burger, donut, burger, burger, donut, donut]
COOK is waiting.
CUST2 ate a burger
Dishes: [donut, burger, burger, donut, donut, burger]
COOK is waiting.
CUST1 ate a donut
Dishes: [burger, burger, donut, donut, burger, donut]
COOK is waiting.
CUST2 ate a burger
Dishes: [burger, donut, donut, burger, donut, donut]
COOK is waiting.
CUST1 ate a donut
Dishes: [burger, donut, burger, donut, donut, donut]
COOK is waiting.
CUST2 ate a burger
Dishes: [donut, burger, donut, donut, donut, burger]
COOK is waiting.
CUST1 ate a donut
Dishes: [burger, donut, donut, donut, burger, donut]
COOK is waiting.
CUST2 ate a burger
Dishes: [donut, donut, donut, burger, donut, donut]
COOK is waiting.
CUST1 ate a donut
Dishes: [donut, donut, burger, donut, donut, burger]
COOK is waiting.
CUST2 ate a burger
Dishes: [donut, donut, donut, donut, burger, donut]
COOK is waiting.
CUST1 ate a donut
Dishes: [donut, donut, donut, burger, donut, donut]
CUST2 ate a burger
Dishes: [donut, donut, donut, donut, donut, burger]
COOK is waiting.
CUST1 ate a donut
Dishes: [donut, donut, donut, donut, burger, donut]
COOK is waiting.
CUST2 ate a burger
Dishes: [donut, donut, donut, donut, donut, donut]
COOK is waiting.
CUST1 ate a donut
Dishes: [donut, donut, donut, donut, donut, donut]
COOK is waiting.
CUST2 is waiting.
CUST1 ate a donut
Dishes: [donut, donut, donut, donut, donut, donut]
CUST2 is waiting.
CUST1 ate a donut
Dishes: [donut, donut, donut, donut, donut, donut]


Lock & Condition

  • wait() & notify()의 단점 : 누구를 깨우는지(waiting pool에 대기중인 쓰레드 중 하나를 깨우는 것이므로) 불분명하다.
    -> Lock & Condition : 구분 가능!!



Ref

0개의 댓글