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
까지는 나오는데 그이상은 에러가 생기면서 안되는데요..
왜그런지 좀... 수정좀 부탁하겠습니다...
이유와 같이요..
내공 빠방하게 겁니다~
답변자님,
정보를 공유해 주세요.
일단, 예외가 발생하는 이유는 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 { class Q { Q() { int get() { void put(int n) { //제어장치 void setStatus(boolean a) { boolean getStatus() { class Producer extends Thread { class Consumer extends Thread { |
굳이 wait/notify를 넣자면 쓰레드 상호간에 "달려"시키거나 따로 "달려"시켜줄 쓰레드를 구현하거나 하면 되겠죠.
코드로 표현하는 방법은 여러가지가 있을 수 있습니다.
상호간에 깨워주는 코드를 작성해보겠습니다. 참고하세요.
class Q { Q() { int get() { void put(int n) { void setStatus(boolean a) { boolean getStatus() { class Producer extends Thread { Producer(Q q) { public void setConsumer(Consumer c) { synchronized void wakeUp() { synchronized public void run() { if (!q.getStatus()) { class Consumer extends Thread { Consumer(Q q) { void setProducer(Producer p) { synchronized void wakeUp() { synchronized public void run() { class PC { public static void main(String[] args) { } |
2009.01.31.
-
채택
질문자가 채택한 답변입니다.
UP이 많은 답변일수록 사용자들에게 더 많이 노출됩니다.
에러가 생기는 이유는
현재 쓰레드가 생산자/ 소비자의 오브젝트 모니터를 가지고 있지 않기 때문에 그렇습니다.
오브젝트 모니터를 가지도록 하려면 생산자/소비자에서 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이 많은 답변일수록 사용자들에게 더 많이 노출됩니다.