NAVER

질문 synchronized producer consumer 질문요 내공 빠방!!!!
바이칸 조회수 1,398 작성일2009.01.31

class Q {
 int n;
 boolean isChanged;
 Q() {
  isChanged = false;
 }

 synchronized int get() {
  System.out.println("Got: " + n);
  return n;
 }
 synchronized void put(int n) {
  this.n = n;
  System.out.println("Put: " + n);
 }

 public synchronized void setStatus(boolean a) {
  this.isChanged = a;
 }

 public boolean getStatus() {
  return isChanged;
 }
}


class Producer implements Runnable {
 Q q;
 Producer(Q q) {
  this.q = q;
  new Thread(this, "Producer").start();
 }

 public void run() {
  int i = 0;
  while (true) {
   while(q.getStatus()) {
    try{
     wait();
    } catch (InterruptedException e) {}
   }
  q.put(i++);
  q.setStatus(true);
  notify();
  }
 }
}


class Consumer implements Runnable {
 Q q;
 Consumer(Q q) {
  this.q = q;
  new Thread(this, "Consumer").start();
 }
 public void run() {
  while (true) {
   while(!q.getStatus()) {
    try {
     wait();
    } catch (InterruptedException e) {}
   }
  q.get();
  q.setStatus(false);
  notify();
  }
 }
}


class PC {
 public static void main(String args[]) {
  Q q = new Q();
  new Producer(q);
  new Consumer(q);
 }
}

 

 

대충 이렇게 짜봤는데요...내용은...

별거 없습니다..

Queue 클래스 Q 는 int n 을 가지고 있습니다.

Producer 클래스는 Q 클래스에 값을 저장하고. (i)

Consumer 클래스는 Producer 가 저장한 값을 읽습니다. (i)

그리고 다시 Producer 는 저장합니다 (i+1) (이렇게 무한반복)

이렇게 서로 교차되면서 해야하며..

중간에 (Producer 를 생산자 / Consumer 를 구매자로 하겠습니다) 생산자가 판매자가 읽지도 않았는데 값을 갱신해서는 안되며..

생산자는 한번 이상 똑같은 값을 읽으면 안됩니다.

 

자바에 입문한지 얼마 안대서..

대충 맞을거라고 코딩했는데..

wait() 와 notify() 에서 문제가 생기고 에러가 생기네요...

Put : 0

Get : 0

까지는 나오는데 그이상은 에러가 생기면서 안되는데요..

왜그런지 좀... 수정좀 부탁하겠습니다...

이유와 같이요..

내공 빠방하게 겁니다~

프로필 사진

답변자님,

정보를 공유해 주세요.

2 개 답변
2번째 답변
프로필 사진
윈리누크
식물신
자바스크립트 6위, 자바, JSP 13위, C, C++ 분야에서 활동
본인 입력 포함 정보

일단, 예외가 발생하는 이유는 wait/notify 메서드를 synchronized 블럭 내에서 사용하지 않기 때문입니다.

다음, 한번만 출력이 되고 멈추는 이유는 둘다 대기를 타기 때문입니다. 논리적 에러라고 할 수 있겠죠.

wait/notify라는 것은 쉽게 얘기하면 "기다려"/"달려~" 뭐 이런건데요.

"기다려" 라는 것은 그 메서드가 호출되는 순간 더 이상 진행되지 못하는 것입니다.

 

Producer 클래스만 한 번 보죠.

 

시작할 때 isChanged = false; 이기 때문에

한 번은 아래 구문의 wait가 실행되지 않습니다.

    while(q.getStatus()) {
     try{
      wait();
     } catch (InterruptedException e) {}
    }

 

q.put(i++);  // 이래서 Put이 하나 출력됩니다.

q.setStatus(true);  // 이래서 다음 회전시에 위 while구문 안의 wait를 실행할 수 있는 여건이 됩니다.

notify();  // 이건 뭘까요? "기다려" 상태도 아닌 애를 "달려~" 라고 합니다. ^^;;

 

다음 회전시엔

wait가 실행됩니다. 그래서 "기다려" 상태가 되죠.

그런데 어느누구도 "달려~"라고 해주지 않습니다.

영원히 "기다려"가 되는거죠.

wait메서드가 호출되는 즉시 더 이상 진행되지 않습니다.

좀 웃기게 표현하자면 wait(); 메서드를 호출하면

세미콜론 뒤에 담배한대 피우면서 쪼그리고 앉아 기다리는 거죠. (아 이런 흡연쓰레드같으니..)

notify를 호출하러 갈 수가 없는 거죠.

 

Consumer는 볼 것도 없죠.

이놈도 쓸모없는 흡연쓰레드입니다.

 

결론적으로 흡연쓰레드를 갈궈서 다시 달리게 할 객체가 필요한 것이죠.

 

하지만, 알고리즘을 보면 wait/notify는 별로 필요한 것 같아 보이지 않습니다.

다음과 같이 질문에 언급하고 있죠.

" Queue 클래스 Q 는 int n 을 가지고 있습니다.

Producer 클래스는 Q 클래스에 값을 저장하고. (i)

Consumer 클래스는 Producer 가 저장한 값을 읽습니다. (i)

그리고 다시 Producer 는 저장합니다 (i+1) (이렇게 무한반복)

이렇게 서로 교차되면서 해야하며..

중간에 (Producer 를 생산자 / Consumer 를 구매자로 하겠습니다) 생산자가 판매자가 읽지도 않았는데 값을 갱신해서는 안되며..

생산자는 한번 이상 똑같은 값을 읽으면 안됩니다. "

질문하신 코드를 보니 Q 클래스에 갱신 여부에 대한 제어 장치를 마련해 두었더군요.

그럼 굳이 쓰레드들이 "기다려"/"달려" 할 필요도 없는 것이죠.

다음 예문과 같이 내가 일을 해도 되면 하고 아니면 말고 그럼 됩니다.

class PC {
 public static void main(String args[]) {
  Q q = new Q();
  new Producer(q).start();
  new Consumer(q).start();
 }
}

class Q {
 int n;
 boolean isChanged;

 Q() {
  isChanged = false;
 }

 int get() {
  System.out.println("Got: " + n);
  return n;
 }

 void put(int n) {
  this.n = n;
  System.out.println("Put: " + n);
 }

 //제어장치

 void setStatus(boolean a) {
  this.isChanged = a;
 }

 boolean getStatus() {
  return isChanged;
 }
}

class Producer extends Thread {
 Q q;
 Producer(Q q) {
  this.q = q;
 }
 public void run() {
  int i=0;
  while(true) {
   if(!q.getStatus()) {
    q.put(i++);
    q.setStatus(true);
   }
  }
 }
}

class Consumer extends Thread {
 Q q;
 Consumer(Q q) {
  this.q = q;
 }
 public void run() {
  while(true) {
   if(q.getStatus()) {
    q.get();
    q.setStatus(false);
   }
  }
 }
}

 

굳이 wait/notify를 넣자면 쓰레드 상호간에 "달려"시키거나 따로 "달려"시켜줄 쓰레드를 구현하거나 하면 되겠죠.

코드로 표현하는 방법은 여러가지가 있을 수 있습니다.

상호간에 깨워주는 코드를 작성해보겠습니다. 참고하세요.

class Q {
 int n;
 boolean isChanged;

 Q() {
  isChanged = false;
 }

 int get() {
  System.out.println("Got: " + n);
  return n;
 }

 void put(int n) {
  this.n = n;
  System.out.println("Put: " + n);
 }

 void setStatus(boolean a) {
  this.isChanged = a;
 }

 boolean getStatus() {
  return isChanged;
 }
}

class Producer extends Thread {
 Q q;
 Consumer c;

 Producer(Q q) {
  this.q = q;
 }

 public void setConsumer(Consumer c) {
  this.c = c;
 }

 synchronized void wakeUp() {
  notify();
 }

 synchronized public void run() {
  int i = 0;
  while (true) {

   if (!q.getStatus()) {
    q.put(i++);
    q.setStatus(true);
    c.wakeUp();
   }
   try {
    wait();
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
  }
 }
}

class Consumer extends Thread {
 Q q;
 Producer p;

 Consumer(Q q) {
  this.q = q;
 }

 void setProducer(Producer p) {
  this.p = p;
 }

 synchronized void wakeUp() {
  notify();
 }

 synchronized public void run() {
  while (true) {
   try {
    wait();
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   if (q.getStatus()) {
    q.get();
    q.setStatus(false);
    p.wakeUp();
   }
  }
 }
}

class PC {

 public static void main(String[] args) {
  Q q = new Q();
  Producer p = new Producer(q);
  Consumer c = new Consumer(q);
  p.setConsumer(c);
  c.setProducer(p);
  p.start();
  c.start();
 }

}

2009.01.31.

  • 채택

    질문자가 채택한 답변입니다.

도움이 되었다면 UP 눌러주세요!
UP이 많은 답변일수록 사용자들에게 더 많이 노출됩니다.
1번째 답변
프로필 사진
an****
중수
자바, JSP 분야에서 활동
본인 입력 포함 정보

에러가 생기는 이유는

현재 쓰레드가 생산자/ 소비자의 오브젝트 모니터를 가지고 있지 않기 때문에 그렇습니다.

오브젝트 모니터를 가지도록 하려면 생산자/소비자에서 syncronized 된 메소드를 호출하면 됩니다.

그래서 public synchronized void run() 이렇게 해주면 됩니다.

쓰레드와 모니터에 관한 내용은

http://blog.naver.com/redmoon0/20054570645

링크를 참조하세요.

 

위 프로그램의 경우 아래처럼 하면 정상 작동합니다.

 

class Q {
 int n;
 boolean isChanged;

 Q() {
  isChanged = false;
 }

 int get() {
  System.out.println("Got: " + n);
  return n;
 }

 void put(int n) {
  this.n = n;
  System.out.println("Put: " + n);
 }

 public void setStatus(boolean a) {
  this.isChanged = a;
 }

 public boolean getStatus() {
  return isChanged;
 }
}

class Producer implements Runnable {
 Q q;

 Producer(Q q) {
  this.q = q;
  new Thread(this, "Producer").start();
 }

 public synchronized void run(){
  int i = 0;
  while (true) {

   while (q.getStatus()) {
    Thread.yield();  //또는 Thread.sleep(0);
   }
   q.put(i++);
   q.setStatus(true);
  }
 }
}

class Consumer implements Runnable {
 Q q;

 Consumer(Q q) {
  this.q = q;
  new Thread(this, "Consumer").start();
 }

 public synchronized void run(){
  while (true) {

   while (!q.getStatus()) {
    Thread.yield();  //또는 Thread.sleep(0);
   }
   q.get();
   q.setStatus(false);
  }
 }
}

class PC {
 public static void main(String args[]) throws Exception{
  Q q = new Q();
  new Producer(q);
  new Consumer(q);
 }
}

2009.01.31.

도움이 되었다면 UP 눌러주세요!
UP이 많은 답변일수록 사용자들에게 더 많이 노출됩니다.