햄찌개

JAVA - 쓰레드 - wait(), notify() 본문

고급 JAVA

JAVA - 쓰레드 - wait(), notify()

햄찌개 2020. 9. 24. 16:13

 

자바의 최상위 클래스인 Object에는 몇 가지 메서드가 존재한다. 널리 쓰이는 toString()은 객체를 문자열로 표현할 때, hashCode()는 객체의 해시 값을 계산할 때 사용된다. 거의 사용되지 않고 가끔 IDE의 자동 완성 기능에서나 보게 되는 메서드들도 있으니 그것이 바로 wait(), notify(), notifyAll()이다. 이들의 동작을 간략히 정리하면 다음과 같다.

 

 

메서드                                           기능                                               비고

wait 갖고 있던 고유 락을 해제하고, 스레드를 잠들게 한다. 호출하는 스레드가 반드시 고유 락을 갖고 있어야 한다. 다시 말해, synchronized 블록 내에서 호출되어야 한다.
notify 잠들어 있던 스레드 중 임의로 하나를 골라 깨운다. 호출하는 스레드가 반드시 고유 락을 갖고 있어야 한다. 다시 말해, synchronized 블록 내에서 호출되어야 한다.
notifyAll 호출로 잠들어 있던 스레드 모두 깨운다. 호출하는 스레드가 반드시 고유 락을 갖고 있어야 한다. 다시 말해, synchronized 블록 내에서 호출되어야 한다.

 

 

 

* wait()메서드 => 동기화 영역에서 락을 풀고 Wait-Set영역(공유객체별 존재)으로 이동시킨다.
 *
 * notify()또는 notifyAll()메서드 => Wait-Set영역에 있는 쓰레드를 꺠워서 실행할 수 있도록 한다.
 * (notify()는 하나, notifyAll()은 Wait-Set에 있는 전부를 꺠운다.)
 *
 * => wait()과 notify(),notifyAll() 메서드는 동기화 영역에서만 실행할 수 있고,
 * Object 클래스에서 제공하는 메서드 이다.

 

예제1)  A가 B를 꺠우고 자로가고 B가 A를 꺠우고 자로가고 하다가 A가 B꺠우기전에 끝나서 B는 않끝남

package kr.or.ddit.basic;

public class T19_WaitNotifyTEst {


	public static void main(String[] args) {
		WorkObject workObj = new WorkObject();
		
		ThreadA tha = new ThreadA(workObj);
		ThreadB thb = new ThreadB(workObj);
		
		tha.start();
		thb.start();
	}

}

// 공통으로 사용할 객체
class WorkObject {
	public synchronized void methodA() {
		System.out.println("methodA()에서 작입중...");

		notify();	//대기실에 있는 놈 깨운다.

		try {
			wait();	//내가 대기실로 간다. 
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public synchronized void methodB() {
		System.out.println("methodB()에서 작입중...");

		notify();

		try {
			wait();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}


/**
 * WorkObject의 methodA()메서드만 호출하는 쓰레드 
 */
class ThreadA extends Thread{
	private WorkObject workObj;

	public ThreadA(WorkObject workObj) {
		this.workObj = workObj;
	}
	
	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			workObj.methodA();
		}
		System.out.println("ThreadA 종료 ...");
	}
	
}

/**
 * WorkObject의 methodB()메서드만 호출하는 쓰레드 
 */
class ThreadB extends Thread{
	private WorkObject workObj;
	
	public ThreadB(WorkObject workObj) {
		this.workObj = workObj;
	}
	
	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			workObj.methodB();
		}
		System.out.println("ThreadB 종료 ...");
	}
	
}

예제1 결과값


package kr.or.ddit.basic;


public class T20_WaitNotifyTest {

	public static void main(String[] args) {

		DataBox dataBox = new DataBox();
		
		ProducerThread pth = new ProducerThread(dataBox);
		ConsumerThread cth = new ConsumerThread(dataBox);
		
		pth.start();
		cth.start();
		
	}

}

//데이터를 공통으로 사용하는 클래스
class DataBox{
	private String  data;
	
	//data가 null일떄 data값을 반환하는 메서드
	public synchronized String getData() {
		if(data == null) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		String returnData = data;
		System.out.println("읽어올데이터 : "+returnData);
		data = null;
		System.out.println(Thread.currentThread().getName()+"notify()호출");
		notify();
		
		return returnData;
	}
	
	//data 가 null일 경우에만 자료를 셋팅하는 메서드
	public synchronized void setData(String data) {
		if(this.data!=null) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.data=data;
		System.out.println("셋팅한 데이터 "+this.data);
		System.out.println(Thread.currentThread().getName()+"notify()호출");
		notify();
	}
}

//데이터를 셋팅만 하는 쓰레드
class ProducerThread extends Thread{
	private DataBox dataBox;

	public ProducerThread(DataBox dataBox) {
		super("ProducerThread");
		this.dataBox = dataBox;
	}
	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			String data = "Data-" +i;
			System.out.println("dataBox.serData("+data+")호출");
			dataBox.setData(data);
		}
	
	}
}

//데이터를 읽어만 오는 쓰레드
class ConsumerThread extends Thread {
	private DataBox dataBox;

	public ConsumerThread(DataBox dataBox) {
		super("ConsumerThread");
		this.dataBox = dataBox;
	}
	@Override
	public void run() {
		for (int i = 1; i <= 10; i++) {
			String data = dataBox.getData();
			System.out.println("dataBox.getData() : "+data);
		}
	}
}