IllegalMonitorStateException없이 Java에서 대기 및 알림을 사용하는 방법은 무엇입니까?


129

나는 2 개의 행렬을 가지고 있으며 그것들을 곱한 다음 각 셀의 결과를 인쇄해야합니다. 하나의 셀이 준비되면 바로 인쇄해야하지만, 예를 들어 [2] [0]의 결과가 먼저 준비된 경우에도 [2] [0] 셀 앞에 [0] [0] 셀을 인쇄해야합니다 . 순서대로 인쇄해야합니다. 그래서 내 생각은 multiplyThread올바른 셀을 인쇄 할 준비가되었다는 알림을 받을 때까지 프린터 스레드를 기다리게 printerThread하고 셀을 인쇄하고 대기 등으로 돌아가는 것입니다.

그래서 곱셈을하는이 스레드가 있습니다.

public void run() 
{
    int countNumOfActions = 0; // How many multiplications have we done
    int maxActions = randomize(); // Maximum number of actions allowed

    for (int i = 0; i < size; i++)
    {       
        result[rowNum][colNum] = result[rowNum][colNum] + row[i] * col[i];
        countNumOfActions++;
        // Reached the number of allowed actions
        if (countNumOfActions >= maxActions)
        {
            countNumOfActions = 0;
            maxActions = randomize();
            yield();
        }   
    }
    isFinished[rowNum][colNum] = true;
    notify();
}

각 셀의 결과를 인쇄하는 스레드 :

public void run()
{
    int j = 0; // Columns counter
    int i = 0; // Rows counter
    System.out.println("The result matrix of the multiplication is:");

    while (i < creator.getmThreads().length)
    {
        synchronized (this)
        {
            try 
            {
                this.wait();
            } 
            catch (InterruptedException e1) 
            {
            }
        }
        if (creator.getmThreads()[i][j].getIsFinished()[i][j] == true)
        {
            if (j < creator.getmThreads()[i].length)
            {
                System.out.print(creator.getResult()[i][j] + " ");
                j++;
            }
            else
            {
                System.out.println();
                j = 0;
                i++;
                System.out.print(creator.getResult()[i][j] + " ");
            }
        }
    }

이제 다음과 같은 예외가 발생합니다.

Exception in thread "Thread-9" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-6" Exception in thread "Thread-4" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-5" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-8" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-7" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-11" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-10" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)
Exception in thread "Thread-12" java.lang.IllegalMonitorStateException
    at java.lang.Object.notify(Native Method)
    at multiplyThread.run(multiplyThread.java:49)

49 줄 multiplyThread은 "notify ()"입니다. 동기화를 다르게 사용해야하지만 어떻게 해야할지 잘 모르겠습니다.

누구나이 코드가 작동하도록 도울 수 있다면 정말 감사하겠습니다.

답변:


215

notify () 를 호출하려면 동일한 객체에서 동기화해야합니다.

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

/* different thread / object */
synchronized (someObject) {
    someObject.notify();
}

29
while(!JobCompleted);옵션은 일반적으로 같은 변수를 지속적으로 검사하는 CPU를 100 %
Matt Lyons

5
while(!JobCompleted) Thread.sleep(5); 문제가 없습니다
BeniBela

15
여전히 완전히 다른 문제가 있습니다. 폴링 (일부 조건이 충족되는지 반복적으로 확인하는 것, 즉 현재하고있는 일)은 일반적으로 상기 조건이 변경되면 (즉, 답변에 요약 한 것) 통지하는 것보다 덜 선호됩니다.
Bombe

3
@huseyintugrulbuyukisik wait현재 스레드가 호출 된 객체에 잠금이있을 때마다 호출 할 수 있습니다 wait. synchronized블록 을 사용하든 동기화 된 방법 을 사용 하느냐 는 전적으로 귀하에게 달려 있습니다.
Bombe

1
@BeniBela 그러나 Huseyin의 원래 질문에 관해서는 확실히 느릴 것으로 예상됩니다.
토마스

64

Java에서 waitand notify또는 notifyAll메소드를 사용하는 동안 다음 사항을 기억해야합니다.

  1. 둘 이상의 스레드가 잠금을 대기 할 것으로 예상되는 경우 notifyAll대신 사용하십시오 notify.
  2. waitnotify방법은 동기화 된 컨텍스트에서 호출되어야합니다 . 자세한 설명은 링크를 참조하십시오.
  3. wait()여러 스레드가 잠금을 기다리고 있고 그 중 하나가 잠금 상태가되어 조건을 재설정하는 경우 다른 스레드가 깨어 난 후 상태를 확인하여 다시 대기해야하는지 여부를 확인해야하기 때문에 항상 루프 에서 메소드를 호출 하십시오. 처리를 시작할 수 있습니다.
  4. 호출 wait()notify()메소드에 동일한 오브젝트를 사용하십시오 . 모든 객체에는 자체 잠금 기능이 있으므로 wait()객체 A와 notify()객체 B를 호출 해도 의미가 없습니다.

21

이걸 전혀 쓰지 않아도 되나요? 행렬이 얼마나 큰지 궁금하고 하나의 스레드 인쇄가 다른 반면 곱셈을 수행하는 데 어떤 이점이 있는지 궁금합니다.

아마도 비교적 복잡한 스레딩 작업을 수행하기 전에 이번에 측정 할 가치가 있습니까?

스레드 해야하는 경우 셀의 곱셈을 수행하기 위해 'n'스레드를 만들고 ( 'n'은 사용 가능한 코어 수입니다) ExecutorServiceFuture 메커니즘을 사용하여 여러 곱셈을 동시에 디스패치합니다 .

그렇게하면 코어 수를 기준으로 작업을 최적화 할 수 있으며 더 높은 수준의 Java 스레딩 도구를 사용하게됩니다 (삶을 더 편하게 만들어야 함). 결과를 다시 수신 행렬에 기록한 다음 모든 향후 작업이 완료되면 간단히 인쇄하십시오.


1
+1 @Greg Brian이 지적한대로 java.util.concurrent 패키지를 살펴 봐야한다고 생각합니다.
ATorras

1
+1하고이 책을 확인하여 wait () 및 notify ()를 사용하는 올바른 방법을 알려줄 것입니다 jcip.net
Chii

14

BlackBoxClassmethod가 있는 클래스가있는 'black box'애플리케이션이 있다고 가정 해 봅시다 doSomething();.

또한 알 수없는 시간 onResponse(String resp)BlackBoxClass지나면 호출 될 관찰자 또는 청취자 가 있습니다 .

흐름은 간단합니다.

private String mResponse = null; 
 ...
BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();
...
@override
public void onResponse(String resp){        
      mResponse = resp;       
}

우리는 무슨 일이 일어나고 있는지 BlackBoxClass, 언제 답변을 받아야 하는지 알지 못하지만 답변을 얻거나 다른 말로 onResponse전화 를받을 때까지 코드를 계속하고 싶지 않다고 가정 해 봅시다 . 여기에 'Synchronize helper'가 입력됩니다.

public class SyncronizeObj {
public void doWait(long l){
    synchronized(this){
        try {
            this.wait(l);
        } catch(InterruptedException e) {
        }
    }
}

public void doNotify() {
    synchronized(this) {
        this.notify();
    }
}

public void doWait() {
    synchronized(this){
        try {
            this.wait();
        } catch(InterruptedException e) {
        }
    }
}
}

이제 원하는 것을 구현할 수 있습니다 :

public class Demo {

private String mResponse = null; 
 ...
SyncronizeObj sync = new SyncronizeObj();

public void impl(){

BlackBoxClass bbc = new BlackBoxClass();
   bbc.doSomething();

   if(mResponse == null){
      sync.doWait();
    }

/** at this momoent you sure that you got response from  BlackBoxClass because
  onResponse method released your 'wait'. In other cases if you don't want wait too      
  long (for example wait data from socket) you can use doWait(time) 
*/ 
...

}


@override
public void onResponse(String resp){        
      mResponse = resp;
      sync.doNotify();       
   }

}

7

모니터를 소유 한 개체에 대해서만 알림을 호출 할 수 있습니다. 그래서 당신은 같은 것이 필요합니다

synchronized(threadObject)
{
   threadObject.notify();
}


3

나는 바로 간단한 예제를 사용하는 올바른 방법 보여주지 waitnotify자바를. ThreadA & ThreadB 라는 두 클래스를 만들겠습니다 . ThreadA는 ThreadB를 호출합니다.

public class ThreadA {
    public static void main(String[] args){
        ThreadB b = new ThreadB();//<----Create Instance for seconde class
        b.start();//<--------------------Launch thread

        synchronized(b){
            try{
                System.out.println("Waiting for b to complete...");
                b.wait();//<-------------WAIT until the finish thread for class B finish
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
} 

클래스 ThreadB의 경우 :

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;
            }
            notify();//<----------------Notify the class wich wait until my    finish 
//and tell that I'm finish
            }
        }
    }

3

스레드를 대안으로 실행하는 방법을 원한다면 간단한 사용 :-

public class MyThread {
    public static void main(String[] args) {
        final Object lock = new Object();
        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "A");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T1").start();

        new Thread(() -> {
            try {
                synchronized (lock) {
                    for (int i = 0; i <= 5; i++) {
                        System.out.println(Thread.currentThread().getName() + ":" + "B");
                        lock.notify();
                        lock.wait();
                    }
                }
            } catch (Exception e) {}
        }, "T2").start();
    }
}

응답 :-

T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B
T1:A
T2:B

동기식으로 수행 할 4 가지 작업이있는 경우 어떻게 작동합니까?
saksham agarwal

2

우리는 notify를 호출하여 대기중인 객체의 실행을 재개 할 수 있습니다.

public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

같은 클래스의 다른 객체에 대해 알림을 호출하여이 작업을 다시 시작하십시오.

public synchronized notifyJoy() {
    joy = true;
    notifyAll();
}

0

이 특정 문제의 경우 다양한 결과를 변수에 저장하지 말고 마지막 스레드가 처리 될 때 원하는 형식으로 인쇄 할 수 있습니다. 다른 프로젝트에서 작업 내역을 사용할 경우 특히 유용합니다.


0

이는 생산자-소비자 패턴의 상황처럼 보입니다. Java 5 이상을 사용하는 경우 블로킹 큐 (java.util.concurrent.BlockingQueue) 사용을 고려하고 스레드 조정 작업을 기본 프레임 워크 / api 구현으로 남겨 둘 수 있습니다. java 5 : http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html 또는 java 7 (같은 예) : http : // docs 의 예제를 참조하십시오 . oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html


0

wait()를 사용하여 메소드 를 호출 할 때 코드 블록을 올바르게 보호 했습니다 synchronized(this).

그러나 notify()보호 블록을 사용하지 않고 메소드 를 호출 할 때 동일한 예방 조치를 취하지 않았습니다 : synchronized(this)또는synchronized(someObject)

Oracle 설명서 페이지를 참조하면 개체 가 포함되어 클래스, wait(), notify(), notifyAll()방법, 당신은이 모든 세 가지 방법에주의 아래에서 볼 수 있습니다

이 메소드는이 객체의 모니터 소유자 인 스레드에 의해서만 호출되어야합니다.

지난 7 년 동안 많은 것들이 바뀌었고 synchronized아래의 SE 질문에서 다른 대안을 살펴 보겠습니다 .

동기화 된 (this)을 사용할 수 있다면 왜 ReentrantLock을 사용합니까?

동기화 및 잠금

Java에서 동기화되지 않습니까?

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.