IllegalMonitorStateExceptionを発生させずにJavaで待機と通知を使用する方法は?
-
23-08-2019 - |
質問
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()」です。同期を別の方法で使用する必要があると思いますが、方法がわかりません。
誰かがこのコードが機能するよう手伝ってくれたら、本当に感謝します。
解決
電話できるようになるには 通知() 同じオブジェクト上で同期する必要があります。
synchronized (someObject) {
someObject.wait();
}
/* different thread / object */
synchronized (someObject) {
someObject.notify();
}
他のヒント
使用中に、 wait
そして notify
または notifyAll
Java のメソッドでは、次のことに留意する必要があります。
- 使用
notifyAll
の代わりにnotify
複数のスレッドがロックを待機していることが予想される場合。 - の
wait
そしてnotify
メソッドは同期されたコンテキストで呼び出す必要があります. 。より詳細な説明については、リンクを参照してください。 - 常に電話してください
wait()
複数のスレッドがロックを待機していて、そのうちの 1 つがロックを取得して条件をリセットした場合、他のスレッドはウェイクアップ後に条件をチェックして、再度待機する必要があるか、処理を開始できるかを確認する必要があるためです。 。 - 呼び出しに同じオブジェクトを使用する
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 つのクラスを作成します。 スレッドA & スレッド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);
}
}
}
クラス 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 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のマニュアルページを参照している場合wait()
、notify()
、notifyAll()
のメソッドが含まれてのクラスは、あなたがすべてのこれらの三つの方法で予防措置の下に見ることができます。
このメソッドは、このオブジェクトのモニターを所有するスレッドによって呼び出されなければなりません。
多くの事は過去7年間に変更され、のは、SEの質問の下でsynchronized
するために、他の選択肢に見てみましょうされています:
なぜReentrantLockのを使用使用する同期(この)?の
同期ロックの対