web-dev-qa-db-ja.com

IllegalMonitorStateExceptionなしでJavaで待機と通知を使用する方法は?

2つのマトリックスがあり、それらを乗算してから各セルの結果を出力する必要があります。 1つのセルの準備ができたらすぐに印刷する必要がありますが、たとえば、[2] [0]の結果が最初に準備できていても、[0] [0]セルをセル[2] [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)

multiplyThreadの49行目は「notify()」です。同期を別の方法で使用する必要があると思いますが、どうすればよいかわかりません。

誰かがこのコードを機能させるのを手伝うことができれば、本当に感謝しています。

126
Greg

notify() を呼び出すには、同じオブジェクトで同期する必要があります。

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

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

JavaでwaitメソッドとnotifyメソッドまたはnotifyAllメソッドを使用する場合、次のことに注意する必要があります。

  1. 複数のスレッドがロックを待機することが予想される場合は、notifyAllの代わりにnotifyを使用します。
  2. waitおよびnotifyメソッドは、同期されたコンテキストで呼び出す必要があります 。より詳細な説明については、リンクを参照してください。
  3. 複数のスレッドがロックを待機しており、そのうちの1つがロックを取得して状態をリセットした場合、他のスレッドは、ウェイクアップ後に状態を確認して、再度待機する必要があるかどうかを確認する必要があるため、常にwait()メソッドを呼び出しますまたは処理を開始できます。
  4. wait()およびnotify()メソッドの呼び出しに同じオブジェクトを使用します。すべてのオブジェクトには独自のロックがあるため、オブジェクトAでwait()を呼び出し、オブジェクトBでnotify()を呼び出しても意味がありません。
63
Jackob

あなたはこれをまったくスレッド化する必要がありますか?私はあなたの行列がどれほど大きいか、そして一方のスレッドが乗算を実行している間にもう一方のスレッドを印刷することに利点があるかどうか疑問に思っています。

おそらく、比較的複雑なスレッド化作業を行う前に、この時間を測定する価値があるでしょうか?

スレッド化する必要がある場合は、「n」スレッドを作成してセルの乗算を実行し(おそらく「n」は使用可能なコアの数です)、 ExecutorService を使用します Future 複数の乗算を同時にディスパッチするメカニズム。

そうすれば、コアの数に基づいて作業を最適化でき、より高いレベルのJavaスレッドツールを使用しています(これにより作業が楽になります)。結果を受信マトリックスに書き戻し、すべてのFutureタスクが完了したらこれを単に印刷します。

20
Brian Agnew

メソッドdoSomething();を持つBlackBoxClassという名前のクラスを持つ「ブラックボックス」アプリケーションがあるとします。

さらに、onResponse(String resp)という名前のオブザーバーまたはリスナーがあり、不明な時間の後にBlackBoxClassによって呼び出されます。

フローは簡単です:

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

BlackBoxClassで何が起こっているのかわからず、いつ答えが得られるのかわからないが、答えを得るまで、または他のWordで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();       
   }

}
13
Maxim Shoustin

モニターを所有するオブジェクトでのみnotifyを呼び出すことができます。のようなものが必要です

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

notify()も同期する必要があります

6
takete.dk

Javaでwaitおよびnotifyを使用する正しい方法を示す簡単な例を示します。そこで、ThreadAThreadBという名前の2つのクラスを作成します。 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
            }
        }
    }

代わりにスレッドを実行する方法が必要な場合は簡単に使用できます:-

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
2
Vijay Gupta

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!");
}

同じクラスの別のオブジェクトでnotifyを呼び出してこれを再開します

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

この特定の問題では、さまざまな結果を変数に保存して、スレッドの最後が処理されたときに、希望する形式で印刷できます。これは、他のプロジェクトで作業履歴を使用する場合に特に便利です。

0
kbluue

wait()を使用してsynchronized(this)メソッドを呼び出すときに、コードブロックを適切に保護しました。

ただし、保護されたブロックを使用せずにnotify()メソッドを呼び出す場合、同じ予防策を講じていません:synchronized(this)またはsynchronized(someObject)

wait()notify()notifyAll()メソッドを含む Object クラスのOracleドキュメントページを参照すると、これら3つのメソッドすべてで以下の予防措置を確認できます。

このメソッドは、このオブジェクトのモニターの所有者であるスレッドによってのみ呼び出される必要があります

過去7年間で多くのことが変更されました。SEの質問の下でsynchronizedに代わる他の方法を見てみましょう。

synchronized(this)を使用できる場合にReentrantLockを使用する理由

同期とロック

Javaでsynchronized(this)を避けますか?

0
Ravindra babu

これは、生産者と消費者のパターンの状況のように見えます。 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
user3044236