wait, notify, 락(데드,라이브)

예숑·2024년 1월 13일

JAVA-Thread

목록 보기
3/4
post-thumbnail

⭐️wait, notify

class Producer extends Thread{
	private MyBox box;
	public Producer(MyBox box) {
		this.box = box;
	}
	public void run() {
		for (int i = 0; i < 20; i++) {
			box.put(i);
			try {
				sleep(100);
			}catch (InterruptedException e) {
			}
		}
	}
}
class Consumer extends Thread{
	private MyBox box;
	public Consumer(MyBox c) {
		box = c;
	}
	public void run() {
		int value = 0;
		for (int i = 0; i < 10; i++) {
			box.get();
			try {
				sleep(100);
			}catch (InterruptedException e) {
			}
		}
	}
}
class MyBox {
	private int contents;
	private boolean isEmpty = true;
	public synchronized void get() {
		//박스가 비지 않았을 때
		if(!isEmpty) {
			isEmpty = !isEmpty;
			System.out.println(Thread.currentThread().getName() + " : 소비 " + contents);
		}
	}
	public synchronized void put(int value) {
		//박스가 비었을 때
		if(isEmpty) {
			contents = value;
			System.out.println(Thread.currentThread().getName() + " : 생산 " + value);
			isEmpty = !isEmpty;
		}
	}
}
public class ProducerConsumer {

	public static void main(String[] args) {
		MyBox c = new MyBox();
		Producer p1 = new Producer(c);
		Consumer c1 = new Consumer(c);
		Consumer c2 = new Consumer(c);
		p1.start();
		c1.start();
		c2.start();

	}
}

→ 위에서 동기화를 해도 컨트롤이 되지 않는다.
상호 배제는 되지만 원하는 순간에 원하는 메소드를 불러 내기는 어렵다.
이럴 때, wait 와 notify 를 사용한다.

class Producer extends Thread{
	private MyBox1 box;
	public Producer(MyBox1 box) {
		this.box = box;
	}
	public void run() {
		for (int i = 0; i < 20; i++) {
			box.put(i);
			try {
				sleep(100);
			}catch (InterruptedException e) {
			}
		}
	}
}
class Consumer extends Thread{
	private MyBox1 box;
	public Consumer(MyBox1 c) {
		box = c;
	}
	public void run() {
		int value = 0;
		for (int i = 0; i < 10; i++) {
			box.get();
			try {
				sleep(100);
			}catch (InterruptedException e) {
			}
		}
	}
}

class MyBox1 {
	private int contents;
	private boolean isEmpty = true;
	public synchronized int get() {
		while(isEmpty) {
			try {
				// 만나면 lock을 풀고 not runnable 로 간다.
				wait();
			}catch (InterruptedException e) {
			}
		}
		isEmpty = !isEmpty;
		// 실행 불가상태로 된 것을 깨는 역할(대기열로 돌아온다.)
		notifyAll();
		System.out.println(Thread.currentThread().getName() + " : 소비 " + contents);
		return contents;
	}
	public synchronized void put(int value) {
		while(!isEmpty) {
			try {
				wait();
			}catch (InterruptedException e) {
			}
		}
		contents = value;
		System.out.println(Thread.currentThread().getName() + " : 생산 " + value);
		isEmpty = !isEmpty;
		notifyAll();
	}
}
public class ProducerConsumer {

	public static void main(String[] args) {
		MyBox1 c = new MyBox1();
		Producer p1 = new Producer(c);
		Consumer c1 = new Consumer(c);
		Consumer c2 = new Consumer(c);
		p1.start();
		c1.start();
		c2.start();

	}
}

wait( ) : wait를 만나면 락을 풀고 실행 불가 상태로 빠진다.
notify, notifyAll( ) : 실행 불가 상태된 애들을 깨워서 대기열로 보내준다.
1) notify( ) : 실행 불가 상태인 애들 중에 무작위로 1개를 깨운다.
2) notifyAll( ) : 실행 불가 상태인 애들 전부를 깨운다.
이때, 무조건 다 깨우는 것이 아니라 누구의 락을 가지고 있는지 보고 깨운다.

예를 들어,
a,b 가 some의 락을 가지고 있고
c,d 가 other의 락을 가지고 있다고 하자.
notifyAll이 some의 락을 가지고 있다면 a,b 만 깨우는 것이다.

wait, notifyAll, notify -> Object에 있다.
자바 모든 객체가 공유자원으로 사용될 준비가 되어 있다.

⭐️데드락(DeadLock)

모두 실행 불가 상태로 되어 버리는 것을 말한다.
스레드의 상태가 변하는 것이 아니다.

⭐️라이브락(LiveLock)

스레드의 상태는 계속 변하는데 코드에 진전이 없는 것을 말한다.

import static java.lang.System.out;


/* This is an example of livelock */
public class Dinner {

    public static void main(String[] args) {
        Spoon spoon = new Spoon();
        Dish dish = new Dish();

        new Thread(new Husband(spoon, dish)).start();
        new Thread(new Wife(spoon, dish)).start();
    }
}


class Spoon {
	private boolean locked;

	public boolean isLocked() {
		return locked;
	}
	public void setLocked(boolean locked) {
		this.locked = locked;
	}
}

class Dish {
    private boolean locked;

	public boolean isLocked() {
		return locked;
	}
	public void setLocked(boolean locked) {
		this.locked = locked;
	}
}

class Husband implements Runnable {

    private Spoon spoon;
    private Dish dish;

    public Husband(Spoon spoon, Dish dish) {
        this.spoon = spoon;
        this.dish = dish;
    }

    @Override
    public void run() {

        while (true) {
            synchronized (spoon) {
                spoon.setLocked(true);
                out.println("husband get spoon");
                try { Thread.sleep(2000); } catch (InterruptedException e) {}

                if (dish.isLocked()) {
                    spoon.setLocked(false); // give away spoon
                    out.println("husband pass away spoon");
                    
                } else {
					synchronized (dish) {
						dish.setLocked(true);
						out.println("Husband is eating!");
					}
					dish.setLocked(false);
				}
            }
            spoon.setLocked(false);
        }
    }
}

class Wife implements Runnable {

    private Spoon spoon;
    private Dish dish;

    public Wife(Spoon spoon, Dish dish) {
        this.spoon = spoon;
        this.dish = dish;
    }

    @Override
    public void run() {
        while (true) {
            synchronized (dish) {
                dish.setLocked(true);
                out.println("wife get dish");
                try { Thread.sleep(2000); } catch (InterruptedException e) {}

                if (spoon.isLocked()) {
                    dish.setLocked(false); // give away dish
                    out.println("wife pass away dish");
                    
                } else {
					synchronized (spoon) {
						spoon.setLocked(true);
						out.println("Wife is eating!");

					}
					spoon.setLocked(false);
				}
            }
            dish.setLocked(false);
        }
    }
}

0개의 댓글