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


129

2つの行列があり、それらを乗算して、各セルの結果を出力する必要があります。1つのセルの準備ができたらすぐに印刷する必要がありますが、たとえば[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);オプションは、同じ変数を常にチェックする100%でCPUを拘束するため、一般に悪い考えです(ここを参照)
Matt Lyons

5
while(!JobCompleted) Thread.sleep(5); その問題はありません
ベニベラ2013

15
それはまだ完全に別のものであるという問題を抱えています。ポーリング(ある条件が満たされているかどうかを繰り返し確認すること、つまり何をしているのか)は、通常、その条件が変更された場合に通知される(つまり、私が回答で概説したもの)よりも優先されません。
Bombe

3
@huseyintugrulbuyukisik wait現在のスレッドが呼び出されたオブジェクトをロックしているときはいつでも呼び出すことができますwaitsynchronizedブロックと同期メソッドのどちらを使用するかは、完全にあなた次第です。
Bombe 14

1
@BeniBelaしかし、(フセインの元の質問に関して)確かに遅くなることが予想できます。
トーマス

64

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

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

21

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

おそらく、比較的複雑なスレッド処理を実行する前に、今回は測定する価値があるでしょうか?

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

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


1
+1 @Gregブライアンが指摘したように、java.util.concurrentパッケージを確認する必要があると思います。
ATorras、2009年

1
+1とこの本もチェックしてください。この本も、wait()とnotify()の正しい使い方を教えてくれますjcip.net
Chii

14

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

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

フローは簡単です:

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

何が起こっているのかBlackBoxClass、いつ答えが得られるのかわからないが、答えが出るまで、つまりgetが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();       
   }

}

7

自分のモニターを所有するオブジェクトでのみnotifyを呼び出すことができます。だからあなたは次のようなものが必要です

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


3

簡単な例waitnotify、Javaでの正しい使用方法を示します。そこで、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
            }
        }
    }

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の質問に対する他の代替案を見てみましょう。

同期(これ)を使用できるのに、なぜReentrantLockを使用するのですか?

同期とロック

Javaで同期(これ)を避けますか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.