자바 - 스레드 동기화 wait()와 notify(), notifyAll() 사용하기
멀티스레드 환경에서 동기화를 하더라도 상황에 따라 여러가지 문제점이 있을 수 있습니다. 그중 하나가 제한된 자원에 접근하는 producer-consumer 문제입니다. 여기서는 이러한 멀티스레드 환경에서 효율적인 동기화를 위해 제공되는 wait()와 notify(), notifyAll() 메소드에 대해서 알아보도록 하겠습니다.
목차
- 스레드 동기화 문제
- wait()와 notify(), notifyAll() 메소드
- 사용예제
1. 스레드 동기화 문제
멀티스레드 환경에서는 synchronized 키워드를 사용하여 동기화를 하더라도 고려해야 할 것들이 많이 있습니다. 그중 하나가 producer-consumer 문제입니다.
예를들어 3개의 상품(product)만 놓아둘 곳이 있는 마켓(market)에 물건을 공급해주는 생산자(producer)와 물건을 소비하는 소비자(consumer)가 있을 경우, 생산자는 3개의 물건이 이미 마켓에 가득 찬 경우에 추가로 물건을 공급할 수 없기 때문에 문제가 생길 수 있고, 소비자는 마켓에 물건이 하나도 없을 경우에 물건을 소비할 수 없기 때문에 문제가 생길수 있습니다.
이러한 경우에 적절한 동기화 처리를 하기 위해서 wait()와 notify(), notifyAll() 메서드가 사용될 수 있습니다.
[해결방안]
1) 마켓에 물건이 가득 찬 경우
- 마켓에 물건이 가득 찼을 경우에는 생산자(producer)가 더이상 생산하지 않고 기다리게 한다. - wait()
- 소비자(consumer)가 물건을 소비한 후에 생산자(producer)에게 알려준다. - notify() or notifyAll()
2) 마트에 물건이 없을 경우
- 마켓에 물건이 없을 경우에는 소비자(consumer)가 더이상 소비하지 못하고 기다리게 한다. - wait()
- 생산자(producer)가 물건을 생산한 후에 소비자(consumer)에게 알려준다. - notify() or notifyAll()
2. wait()와 notify(), notifyAll() 메소드
이전에 Object 클래스에 대해 알아보았을 때, Object 클래스에 스레드와 관련하여 wait()와 notify(), notifyAll() 메서드가 있다는 것을 확인할 수 있었습니다. Object 클래스는 최상위 클래스로 모든 객체가 상속을 받는 클래스이기 때문에 모든 객체는 동기화 객체로 사용시 해당 메소드를 사용할 수 있습니다. 또한 스레드와 관련된 이 메소드들은 synchronized로 지정된 임계영역(critical section) 안에서만 사용이 가능합니다.
- void wait() : 현재 스레드를 다른 스레드가 이 객체에 대한 notify() 또는 notifyAll() 메소드를 호출할때까지 대기합니다.
- void wait(long timeout) : 현재 스레드를 다른 스레드가 이 객체에 대한 notify() 또는 notifyAll() 메소드를 호출하거나 timeout 시간동안 대기합니다.
- void notify() : 이 객체에 대해 대기중인 스레드 하나를 깨웁니다.
- void notifyAll() : 이 객체에 대해 대기중인 모든 스레드를 깨웁니다.
3. 사용예제
1) 클래스 정의
public class Market {
private int count_product = 0;
final private int MAX = 3;
final private int MIN = 0;
synchronized public void add() {
System.out.print("[추가전" + count_product + "]");
while (count_product >= MAX) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count_product++;
notifyAll();
System.out.print("[추가후" + count_product + "]");
}
synchronized public void remove() {
System.out.print("[삭제전" + count_product + "]");
while (count_product <= MIN) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count_product--;
notifyAll();
System.out.print("[삭제후" + count_product + "]");
}
synchronized public int getCount_product() {
return count_product;
}
}
class ProducerThread extends Thread {
Market m;
public ProducerThread(Market m) {
this.m = m;
}
@Override
public void run() {
for (int i=0; i<10; i++) {
HelloWorld.threadSleep((int)(Math.random()*10));
m.add();
}
}
}
class ConsumerThread extends Thread {
Market m;
public ConsumerThread(Market m) {
this.m = m;
}
@Override
public void run() {
for (int i=0; i<10; i++) {
HelloWorld.threadSleep((int)(Math.random()*10));
m.remove();
}
}
}
2) 객체 생성 및 실행
public class HelloWorld {
public static void main(String[] args) {
Market m = new Market();
ProducerThread pt = new ProducerThread(m);
ConsumerThread ct = new ConsumerThread(m);
pt.start();
ct.start();
try {
pt.join();
ct.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%nResult: " + m.getCount_product());
}
static void threadSleep(long time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
3) 실행결과
[추가전0][추가후1][추가전1][추가후2][추가전2][추가후3][추가전3][삭제전3][삭제후2][추가후3]
[삭제전3][삭제후2][삭제전2][삭제후1][삭제전1][삭제후0][추가전0][추가후1][삭제전1][삭제후0]
[추가전0][추가후1][추가전1][추가후2][추가전2][추가후3][삭제전3][삭제후2][추가전2][추가후3]
[삭제전3][삭제후2][추가전2][추가후3][삭제전3][삭제후2][삭제전2][삭제후1][삭제전1][삭제후0]
Result: 0
'IT 개발 > Java' 카테고리의 다른 글
[Java] 자바 - 멀티채팅 프로그램 구현하기 (0) | 2022.08.10 |
---|---|
[Java] 자바 - 소켓통신이란? 소켓(Socket) 개념과 사용방법 (2) | 2022.08.08 |
[Java] 자바 - Synchronized 스레드 동기화 개념 및 사용예제 (0) | 2022.08.03 |
[Java] 자바 - 데몬스레드 vs 사용자스레드 (daemon thread vs user thread) (0) | 2022.08.01 |
[Java] 자바 - Thread란? 스레드 개념 및 사용방법 (2) | 2022.08.01 |