[완료] 자바 스레드 공부하는 도중에 막힌 소스인데 좀 봐주세요

tyolee83의 이미지

class Producer extends Thread
{
    public void run()
    {
        for(int i=0; i<10; i++)
        {
            Buffer.put(i);

            System.out.println("생산자 : 생산 " + i);
            try
            {
                sleep((int)(Math.random() * 100));
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }
    }
}

class Consumer extends Thread
{
    public void run()
    {
        int value = 0;
        for(int i=0; i<10; i++)
        {
            value = Buffer.get();

            System.out.println("소비자 : 소비 " + value);
        }
    }
}

class Buffer
{
    static private int contents;
    static private boolean available = false;
    //flag 역할을 하는 available 변수의 초기 값을 false로 설정
    //처음에는 생산자가 먼저 데이터를 가져다 놓아야 한다

    static public synchronized int get()
    {
        if(available == false)
        {
            try
            {
                Thread.currentThread().wait();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }

        available = false;
       Thread.currentThread().notifyAll();
        return contents;
    }

    static public synchronized void put(int value)
    {
        if(available == true)
        {
            try
            {
                Thread.currentThread().wait();
            }
            catch(Exception e)
            {
                e.printStackTrace();
            }
        }

        contents = value;
        available = true;
        Thread.currentThread().notifyAll();
    }
}

public class ProducerConsumer
{
    public static void main(String args[])
    {
        Producer p1 = new Producer();
        Consumer c1 = new Consumer();

        p1.start();
        c1.start();
    }
}

많이들 아시는 생산자-소비자를 스레드로 짠건데요

버퍼는 클래스 메소드로 짰구요...

근데 이런 에러가 나네요....

Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at Buffer.get(ProducerConsumer.java:59)
at Consumer.run(ProducerConsumer.java:29)
java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at Buffer.put(ProducerConsumer.java:80)
at Producer.run(ProducerConsumer.java:7)

아무리 봐도 뭐가 틀린지 모르겠는데....

좀 도와주세요 ㅠㅠ

pool007의 이미지

class Producer extends Thread 
{ 
    public void run() 
    { 
        for(int i=0; i<10; i++) 
        { 
            Buffer.put(i); 

            System.out.println("생산자 : 생산 " + i); 
            try 
            { 
                sleep((int)(Math.random() * 100)); 
            } 
            catch(Exception e) 
            { 
                e.printStackTrace(); 
            } 
        } 
    } 
} 

class Consumer extends Thread 
{ 
    public void run() 
    { 
        int value = 0; 
        for(int i=0; i<10; i++) 
        { 
            value = Buffer.get(); 

            System.out.println("소비자 : 소비 " + value); 
        } 
    } 
} 

class Buffer 
{ 
    static private int contents; 
    static private boolean available = false; 
    //flag 역할을 하는 available 변수의 초기 값을 false로 설정 
    //처음에는 생산자가 먼저 데이터를 가져다 놓아야 한다 

    static public synchronized int get() 
    { 
        if(available == false) 
        { 
            try 
            { 
                Buffer.class.wait(); 
            } 
            catch(Exception e) 
            { 
                e.printStackTrace(); 
            } 
        } 

        available = false; 
        Buffer.class.notifyAll(); 
        return contents; 
    } 

    static public synchronized void put(int value) 
    { 
        if(available == true) 
        { 
            try 
            { 
            	Buffer.class.wait();
            } 
            catch(Exception e) 
            { 
                e.printStackTrace(); 
            } 
        } 

        contents = value; 
        available = true; 
        Buffer.class.notifyAll(); 
    } 
} 

public class ProducerConsumer 
{ 
    public static void main(String args[]) 
    { 
        Producer p1 = new Producer(); 
        Consumer c1 = new Consumer(); 

        p1.start(); 
        c1.start(); 
    } 
} 

여기서 모니터에 사용되는 객체는 Buffer가 되야겠죠. 즉, 버퍼에는 하나의 쓰레드만 들어갈 수 있게요.

그러려면

static public synchronized ... (...)

메소드를 쓰신 부분은 맞고요. 이 경우 Buffer 클래스에대한 락이 걸리죠. 그리고 구체적으로는 Buffer.class 라는 '클래스 인스턴스'에 대한 락이 걸립니다. (여기서 abc.class 는 abc라는 클래스를 설명하는 java.lang.Class 라는 클래스에대한 인스턴스입니다.)

그리고 wait는 역시 버퍼라는 클래스 전체에 걸리는 것이니 Buffer.class.wait 이구요. 락을 풀때도 Buffer.class.notifyAll 입니다.

Thread.currentThread().notifyAll 은 현재 쓰레드에 대해서 notify 한다는 것인데, 잘 보시면 Producer와 Consumer는 '서로다른' 쓰레드이죠. 그리고 서로다른 쓰레드가 각자 자신에게 락을 걸고 자신의 락을 푼다면 서로 상대를 제어할 수 없죠.

'공유되는 자원'자체에 대해서 lock걸고 wait하고 notify 하는 것이 바른 방법입니다.

--
Passion is like genius; a miracle.

익명 사용자의 이미지

Exception 설명에서도 나온 용어이긴 한데요...

스레드에서 모니터라는게

무얼 의미하는 거죠?

동기화 되는 대상을 말하는건가요?

아 어렵네요 ㅠㅠ

tyolee83의 이미지

정말 Buffer.class.~~ 이렇게 고치니까

신기하게도 되네요

그런데 용어가 좀 생소해서요

1. 스레드에서 모니터라는 용어가

동기화 되는 부분을 말하는 건가요? 잘 모르겠네요 -_-a

2. Buffer.class 라는게 Buffer 의 인스턴스라고 하셨는데

그부분에 대해서 조금만 더 설명해 주시면 안될까요?

처음 보는 문법이고, 클래스 인스턴스라는 말도 처음 들어봐서요;;

클래스 인스턴스라....

조금만 더 도와주셨으면 합니다^^

감사합니다

//TODO

채영구의 이미지

"monitor는 모든 java object 에 붙어있다" 라는 것이 있고
"어떤 object의 monitor는 하나의 스레드만 소유할 수 있다" 라고만 일단 기억해두시고
"wait(), notify(), notifyAll() 등을 호출할 때는 해당 object의 monitor를 가지고 있어야 한다" 라고도 기억해 두고
"monitor 를 소유하려면 해당 object 에 대해 synchronized method 나 block 을 사용하면된다" 여기까지 정리합니다.

lock 을 잡고 푼다는 것은 java 에서 monitor 를 소유한다는 개념으로 구현해놓았고
wait(), notify() 등은 다음과 같이 사용해야 합니다.

synchronized(object) {
    object.wait();
}

...

synchronized(object) { 
    object.notifyAll();
}

public void synchronized someMethod() {} 는
public void someMethod() { synchronized(this) {} } 와 같습니다.

채영구의 이미지

Buffer.class 로 수정해서 작동하게 한 것은 그야말로 작동만 잘 하는 코드입니다.
먼저 질문자께서 Buffer class 에 static method 를 집어넣으면서 코딩한 것이
문제이긴 한데... 굳이 이런 예를 가지고 공부하는 것은 좋지 않다고 봅니다.

동기화 부분과는 다른 문제이고 static method, static class 등에 대한 개념이 잘 잡힌다면
그 때 이해할 수 있을 것입니다.

tyolee83의 이미지

원래 예제는요

각 클래스마다 Buffer blank 이렇게 객체 생성 하고

Producer, Consumer 객체를 생성할 때도

blank를 인수로 넘기더라구요

근데 굳이 꼭 그럴 필요 없이

클래스 메소드로 써도 될것 같아서

이렇게 바꿔본건데...

클래스 메소드를 사용하는 것이 안좋은건가요? ㅠㅠ

//TODO

채영구의 이미지

static 변수나 클래스를 남발하는 것은
c 에서 전역변수를 남발하는 것과 마찬가지 입니다.
디버깅하기 힘들고 나중에 수정하기도 어려워집니다.
수정은 여기서 했는데 문제는 엉뚱한 곳에서 터지죠.

그리고 원래 예제를 그렇게 변경하신 거라면
현재 프로그램은 원래의 의도와 다르게 작동합니다.

Buffer b1 = new Buffer();
Producer p1 = new Producer(b1);
Consumer c1 = new Consumer(b1);

Buffer b2 = new Buffer();
Producer p2 = new Producer(b2);
Consumer c2 = new Consumer(b2);

원래 프로그램은 대략 저런 모양이었을 것이고
여기서는 p1 이 put() 한 내용을 c1 만 get() 합니다.
p2가 put() 한 내용은 c2 만 get() 하겠지요.

바뀐 프로그램에서는 Buffer 가 static 이고, 공유되어 있으므로
p1, p2, c1, c2 가 서로 put(), get() 하는 내용이 섞여버릴 것입니다.

익명 사용자의 이미지

ㅇㄱ wrote:
Exception 설명에서도 나온 용어이긴 한데요...

스레드에서 모니터라는게

무얼 의미하는 거죠?

동기화 되는 대상을 말하는건가요?

아 어렵네요 ㅠㅠ

* java vm은 준-OS라고 볼 수 있습니다. 리소스관리를 vm이 하지요. 모니터 개념은 운영체제의 기본개념이라고 볼 수 있습니다.

*참고 :
http://www.cs.umass.edu/~mcorner/courses/691J/papers/TS/hoare_monitors/hoare_monitors.pdf
고전인데, 운영체제 개념잡기에 훌륭한 ....

shamlock의 이미지

동의어는 아니지만 개념상 락이랑 비슷합니다.

교수님한테 모니터와 락의 차이가 무엇이냐? 고 물어봣었는데

거의 같은 것이고...
한가지 어떤저떤 차이로 두가지를 구분한다고 말씀해주시긴했는데
제가 그때 이해하고 까먹었네요

@.@

kfmes의 이미지

lyk21024 wrote:
원래 예제는요

각 클래스마다 Buffer blank 이렇게 객체 생성 하고

Producer, Consumer 객체를 생성할 때도

blank를 인수로 넘기더라구요

근데 굳이 꼭 그럴 필요 없이

클래스 메소드로 써도 될것 같아서

이렇게 바꿔본건데...

클래스 메소드를 사용하는 것이 안좋은건가요? ㅠㅠ


Buffer blank
자바에서는
이렇게만 하면, blank라는 객체가 생성이 안되고
blank라는 이름을 Buffer 이라는 형태로 쓰겠다고 선언만 해주는겁니다.

blank = new Buffer(); 이런식으로 쓰면 이때야 비로소 Buffer 이라는 형태의 객체가 만들어지면서 blank가 그 생성된곳을 가리키죠

저도 지난학기 자바 수업을 들으면서 알게된겁니다만,
함수를 부를때는 call by value, 와 call by reffer 이렇게 있는데,
자바에서는 여기서 primitive data type(int, char, long etc..)등과 같은 데이터들은 call by value 로 넘어가고, 이 외의 다른것들은 참조 하고 있는 reffer 를 넘겨줍니다(c언어에서 포인터와 비슷한개념으로 보시면 됩니다)

main에서 생성된 객체를 다른 클래스에서 사용을 하려고,
생성자에 생성된 객체를 넘겨주는걸로 보여집니다.

아마 원래소스는

class Producer extends Thread{
Buffer blank;
public Producer(Buffer blank){
this.blank = blank;
}

이런식이었지 싶습니다.

이렇게 하면 넘겨받은 blank의 객체가 ,
Producer에서 동일한 곳을 가리키게 되므로
-_ - 굳이 Static Method 를 안써도 ..........

----------------------------------------

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.