wait()와 notify()는 동일한 잠금 객체 하에서 동기화되어 실행되는 블록에서 실행될 수 있다.
notify() 이후의 실행 순서는 먼저 notify()를 호출한 쓰레드가 synchronized 블록을 벗어난 다음에 wait()하다 깨어난 쓰레드의 수행을 보장하는 순서로 된다.
|
참고: Mesa 방식과 Hoare 방식 Mesa 방식에서는 wait()하던 쓰레드가 다시 잠금을 획득하는 것은 순전히 wait()하던 객체의 책임이다. 즉, notify()하던 쓰레드는 wait()하는 객체를 잠금을 획득하려고 하는(스케쥴되려고 하는) 쓰레드 목록에 올려줄 뿐, 자신의 임계영역(critical section)을 계속 수행한 다음 임계 영역을 벗어나면서 잠금을 푼다. Hoare 방식에서는 notify()하던 쓰레드가 잠시 잠금을 풀고 제어를 깨어날 쓰레드에게 넘겨준다. wait()하던 객체가 임계영역 수행을 마친 후 다시 notify()했던 쓰레드에게 잠금에 대한 제어를 돌려준다. Mesa 방식을 사용하게 되면 깨어난 쓰레드가 실행되기 전에 어떤 다른 쓰레드가 잠금을 획득하여 수행되면서 데이터를 변경시킬 수 있는 단점이 있다. 그러나 Hoare 방식에 비해 구현이 훨씬 쉽다는 장점이 있다.
자바의 구현은 Mesa 방식이다. 각 잠금 객체에는 기다리는 쓰레드 목록(wait set)이 있어서 notify()를 하면 wait set에 있는 쓰레드들 중 임의의 하나를 wait set에서 지워서 다시 스케쥴링될 수 있도록 한다. 물론 notify()한 쓰레드가 임계영역을 벗어나 잠금을 풀지 않는 한 스케쥴링된 쓰레드가 잠금을 획득할 수는 없다.
|
쓰레드가 wait()에 도달하면 잠금 객체를 풀고 notify()되기를 기다리다가 notify()로 인해 깨어난 다음에 자동으로 다시 잠금 객체를 잠그고 나머지 synchronized 블록 내부를 수행하게 된다.
유닉스 POSIX 쓰레드 프로그래밍을 할 때의 pthread_cond_signal()과 pthread_cond_wait() 함수가 각각 자바의 notify(), wait()에 해당한다고 볼 수 있다.
/*
* WaitTest.java
* Copyright 1998 yoonforh@yahoo.com
*/
class WaitTest {
int value;
class Putter extends Thread {
Object locker;
public Putter(Object locker) {
this.locker=locker;
}
public void run() {
doPut(locker);
}
}
void doPut(Object obj) {
synchronized (obj) {
value++;
System.out.println("PUTTER["+Thread.currentThread().getName()+"]:value="+value);
notify();
System.out.println("PUTTER["+Thread.currentThread().getName()+"]:after notify");
}
}
class Getter extends Thread {
Object locker;
public Getter(Object locker) {
this.locker=locker;
}
public void run() {
doGet(locker);
}
}
void doGet(Object obj) {
synchronized (obj) {
System.out.println("GETTER["+Thread.currentThread().getName()+"]:before wait");
try {
wait();
} catch (InterruptedException ie) {
System.out.println("Interrupted...");
}
value--;
System.out.println("GETTER["+Thread.currentThread().getName()+"]:value="+value);
}
}
public WaitTest() {
Thread one = new Putter(this);
Thread two = new Putter(this);
Thread three = new Putter(this);
Thread four = new Putter(this);
Thread five = new Putter(this);
Thread six = new Getter(this);
Thread seven = new Getter(this);
Thread eight = new Getter(this);
Thread nine = new Getter(this);
Thread ten = new Getter(this);
six.start();
seven.start();
eight.start();
nine.start();
ten.start();
one.start();
two.start();
three.start();
four.start();
five.start();
}
public static void main(String args[]) {
new WaitTest();
}
}
실행 결과 예 1(Digital Unix 4.0B(1CPU) JDK 1.1.6에서 실행)
GETTER[Thread-7]:before wait GETTER[Thread-8]:before wait GETTER[Thread-9]:before wait GETTER[Thread-10]:before wait GETTER[Thread-11]:before wait PUTTER[Thread-2]:value=1 PUTTER[Thread-2]:after notify GETTER[Thread-7]:value=0 PUTTER[Thread-3]:value=1 PUTTER[Thread-3]:after notify PUTTER[Thread-4]:value=2 PUTTER[Thread-4]:after notify GETTER[Thread-8]:value=1 PUTTER[Thread-5]:value=2 PUTTER[Thread-5]:after notify GETTER[Thread-9]:value=1 PUTTER[Thread-6]:value=2 PUTTER[Thread-6]:after notify GETTER[Thread-10]:value=1 GETTER[Thread-11]:value=0실행 결과 예 2(Digital Unix 4.0B(1CPU) JDK 1.1.6에서 실행)
GETTER[Thread-7]:before wait PUTTER[Thread-2]:value=1 PUTTER[Thread-2]:after notify PUTTER[Thread-3]:value=2 PUTTER[Thread-3]:after notify <- wait하고 있는 쓰레드가 없는데 notify PUTTER[Thread-4]:value=3 PUTTER[Thread-4]:after notify <- wait하고 있는 쓰레드가 없는데 notify GETTER[Thread-7]:value=2 GETTER[Thread-8]:before wait PUTTER[Thread-5]:value=3 PUTTER[Thread-5]:after notify GETTER[Thread-9]:before wait PUTTER[Thread-6]:value=4 PUTTER[Thread-6]:after notify GETTER[Thread-10]:before wait <- 이 쓰레드는 무한히 기다리게 된다. GETTER[Thread-8]:value=3 GETTER[Thread-11]:before wait <- 이 쓰레드는 무한히 기다리게 된다. GETTER[Thread-9]:value=2
/*
* condtest.c
* Copyright 1998 yoonforh@yahoo.com
*/
#include <pthread.h>
#include <stdio.h>
/******************************** DEFINES *****************************/
#define NUMTHREADS 10
#define NUM_PUTTERS 5
#define NUM_GETTERS 5
pthread_mutex_t mutex;
pthread_cond_t cond;
int value=0;
main(int argc, char **argv)
{
int numThr;
pthread_t threadId[NUMTHREADS];
pthread_attr_t threadAttr;
void *Getters();
void *Putters();
int i;
int *status;
pthread_mutexattr_t *m_attr;
pthread_condattr_t *c_attr;
m_attr=(pthread_mutexattr_t *) malloc(sizeof(pthread_mutexattr_t));
c_attr=(pthread_condattr_t *) malloc(sizeof(pthread_condattr_t));
pthread_mutexattr_init(m_attr);
pthread_condattr_init(c_attr);
pthread_mutex_init(&mutex , m_attr);
pthread_cond_init(&cond , c_attr);
numThr = NUM_PUTTERS + NUM_GETTERS;
if (numThr > NUMTHREADS)
exit(0);
pthread_attr_init(&threadAttr);
for (i = 0; i < NUM_PUTTERS; ++i)
pthread_create(&threadId[i], &threadAttr, Getters, NULL);
for (i= NUM_PUTTERS ; i < numThr; ++i)
pthread_create(&threadId[i], &threadAttr, Putters, NULL);
for (i = 0; i < numThr; ++i)
pthread_join(threadId[i],(void**) &status);
exit(0);
}
void *
Putters(void *pThrArg)
{
pthread_t Tid;
int i;
char msg[256];
Tid = pthread_self();
pthread_mutex_lock(&mutex);
value++;
fprintf(stderr, "PUTTER[%d]:value=%d\n", Tid, value);
pthread_cond_signal(&cond);
fprintf(stderr, "PUTTER[%d]:after cond_signal\n", Tid);
pthread_mutex_unlock(&mutex);
return(NULL);
}
void *
Getters(void *pThrArg)
{
volatile int i;
int nmsgs;
int Rc;
char msg[256];
pthread_t Tid;
Tid = pthread_self();
pthread_mutex_lock(&mutex);
fprintf(stderr, "GETTER[%d]:before cond_wait\n", Tid);
pthread_cond_wait(&cond, &mutex);
value--;
fprintf(stderr, "GETTER[%d]:value=%d\n", Tid, value);
pthread_mutex_unlock(&mutex);
return(NULL);
}
실행 결과 예(Digital Unix 4.0B(1CPU)에서 실행)
GETTER[1073797136]:before cond_wait GETTER[1073840176]:before cond_wait GETTER[1073841552]:before cond_wait GETTER[1073842928]:before cond_wait GETTER[1073844304]:before cond_wait PUTTER[1073845680]:value=1 PUTTER[1073845680]:after cond_signal PUTTER[1073889328]:value=2 PUTTER[1073889328]:after cond_signal PUTTER[1073890704]:value=3 PUTTER[1073890704]:after cond_signal PUTTER[1073892080]:value=4 PUTTER[1073892080]:after cond_signal PUTTER[1073893456]:value=5 PUTTER[1073893456]:after cond_signal GETTER[1073797136]:value=4 GETTER[1073840176]:value=3 GETTER[1073841552]:value=2 GETTER[1073842928]:value=1 GETTER[1073844304]:value=0