불법 모니터 스테이트 exception없이 Java에서 대기 및 통지 방법을 사용하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/886722

문제

매트릭스 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 ()"입니다. 동기화 된 것을 다르게 사용해야한다고 생각하지만 어떻게 확실하지 않습니다.

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

도움이 되었습니까?

해결책

전화 할 수 있습니다 notify () 동일한 객체에서 동기화해야합니다.

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

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

다른 팁

사용하는 동안 wait 그리고 notify 또는 notifyAll Java의 방법 다음과 같은 사항을 기억해야합니다.

  1. 사용 notifyAll 대신에 notify 하나 이상의 스레드가 잠금을 기다릴 것으로 예상하면.
  2. 그만큼 wait 그리고 notify 방법은 동기화 된 컨텍스트에서 호출되어야합니다. 자세한 설명은 링크를 참조하십시오.
  3. 항상 전화하십시오 wait() 여러 스레드가 잠금을 기다리고 있고 그 중 하나가 잠금 장치를 가져 와서 조건을 재설정하면 다른 스레드가 다시 대기 해야하는지 또는 처리를 시작할 수 있는지 확인하기 위해 조건을 점검해야하기 때문에 루프에서 방법. .
  4. 호출에 동일한 개체를 사용하십시오 wait() 그리고 notify() 방법; 모든 객체에는 자체 잠금 장치가있어 호출이 있습니다 wait() 객체 A와 notify() 객체 B에서는 아무 의미가 없습니다.

이것을 전혀 쓸 필요가 있습니까? 나는 당신의 매트릭스가 얼마나 큰지, 그리고 하나의 스레드 프린트가있는 반면, 다른 스레드가 곱셈을 할 수 있는지 궁금합니다.

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

실을 실행 해야하는 경우 셀의 곱셈을 수행하기 위해 'N'스레드를 생성하고 (아마도 'N'은 사용 가능한 코어의 수)를 사용합니다. ExecutorService 그리고 미래 여러 개의 곱셈을 동시에 파견하는 메커니즘.

이렇게하면 코어 수에 따라 작업을 최적화 할 수 있으며 더 높은 수준의 Java 스레딩 도구 (수명을 더 쉽게 만들어야 함)를 사용합니다. 결과를 수신 행렬에 다시 작성한 다음 모든 미래의 작업이 완료되면 간단히 인쇄하십시오.

일부 클래스가있는 '블랙 박스'응용 프로그램이 있다고 가정 해 봅시다. BlackBoxClass 그것은 방법이 있습니다 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 전화. 여기에 '동기화 도우미'를 입력하십시오.

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();       
   }

}

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

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

notify() 동기화해야합니다

올바른 간단한 예제는 올바른 사용 방법을 보여줍니다. wait 그리고 notify 자바에서. 그래서 2 개의 클래스를 만들겠습니다 스레다 & 스레드 B. 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);
        }
    }
} 

그리고 클래스 스레드의 경우 :

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
            }
        }
    }

대기 개체의 실행을 다시 시작하기 위해 알림에 전화 할 수 있습니다.

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();
}

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

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

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

이것은 생산자 소비자 패턴의 상황처럼 보입니다. 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

전화 할 때 코드 블록을 올바르게 보호했습니다. wait() 사용하여 방법 synchronized(this).

하지만 전화 할 때 동일한 예방 조치를 취하지 않았습니다. notify() 가드 블록을 사용하지 않고 방법 : synchronized(this) 또는 synchronized(someObject)

Oracle Documentation 페이지를 참조하는 경우 물체 포함 된 클래스 wait() ,notify(), notifyAll() 방법,이 세 가지 방법 모두에서 아래 예방 조치를 볼 수 있습니다.

이 방법은이 개체의 모니터의 소유자 인 스레드에 의해서만 호출되어야합니다.

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

동기화 된 (this)를 사용할 수있는 경우 재진입 원동력을 사용하는 이유는 무엇입니까?

동기화 대 잠금

자바에서 동기화 (이것)을 피하십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top