私はしばしば閲覧します。 JavaのNotifyとNotifyAllについて、人々はしばしば次の記述を持っています。
Notifyは待機しているオブジェクトにのみ通知し、NotifyAllはすべてのオブジェクトが待機していることを通知し、すべてのオブジェクトが実行され続けます。
そして、それを証明する例があるようです。上記の声明は正しいと言えます。その理由は、それらの1つが非常に重要であるためです。公式声明は次のとおりです。
待って、notify、notifyall:
この方法は、このオブジェクトモニターの所有者であるスレッドでのみ呼び出される必要があります。スレッドは、3つの方法のいずれかでこのオブジェクトモニターの所有者になることができます。
このオブジェクトの同期インスタンスメソッドを実行します。
同期されたステートメントの本文は、このオブジェクトで同期されたステートメントを実行することにより実行されます。
タイプクラスのオブジェクトの場合、クラスの同期静的メソッドを実行できます。
一度にオブジェクトモニターがあるスレッドは1つだけです。
上記の声明は、Javadocから抜粋されています。これは、コールでは、オブジェクトモニター(つまり、ロック)を保持する必要があることを意味します。これは、同期された方法内で実行されると理解できます。次に、このステートメントの暗黙の意味は、同期ブロックに含まれるコードブロックを続行する場合は、ロックを再取得する必要があるということです。この文はJavadocで説明されています:
待って
この方法により、現在のスレッド(Tと呼ばれる)がオブジェクトの待機セットにそれ自体を配置し、このオブジェクトのすべての同期要件を放棄します。スレッドスケジューリングの目的では、スレッドTは無効になり、次の4つの状況のいずれかが発生する前に冬眠中です。
他のいくつかのスレッドは、このオブジェクトのNotifyメソッドを呼び出し、スレッドTはオプションでウェイクアップスレッドとして選択されます。
他のいくつかのスレッドは、このオブジェクトのnotifyallメソッドを呼び出します。
他のいくつかのスレッドはスレッドT.
指定された実際の時間に到達しました。ただし、タイムアウトがゼロの場合、実際の時間は考慮されず、通知が取得されるまでスレッドが待機します。
次に、スレッドTがオブジェクトの待機セットから削除され、スレッドスケジューリングが再び実行されます。スレッドは、オブジェクトで同期する権利を取得するために、従来の方法で他のスレッドと競合します。オブジェクトの制御が取得されると、オブジェクト上のすべての同期宣言が以前の状態に復元されます。
メソッドがある状況。スレッドTは、コールから待機方法に戻ります。したがって、待機方法から戻るとき、オブジェクトとスレッドTの同期状態は、待機方法を呼び出すときとまったく同じです。
つまり、notifyallの場合、すべてのスレッドに通知されるため、ロックを再取得する必要があります。ただし、これらのスレッドは競合し、1つのスレッドのみがロックを正常に取得します。このスレッドが実行される前に、他のスレッドが待機する必要があります(ただし、ここで通知を通知する必要はありません。これは通知であり、ロックを取得するだけであるためです)。この現象を再現できるコードがあります。
まず、次のように実行できるスレッドクラスを定義します。
private static final object obj = new object();静的クラスrは実行可能{int i; r(int i){this.i = i; } public void run(){try {synchronized(obj){system.out.println( "thread->" + i + "waiting"); obj.wait(); System.out.println( "thread->" + i + "running"); thread.sleep(30000); }} catch(例外e){e.printstacktrace(); }}}上記の実行方法の内部に注意してください。待機した後()、文を印刷してから、現在のコードを30秒間一時停止します。睡眠方法については、次のように説明されています。
スレッドは、モニターの所有権を失うことはありません。
つまり、ロックはまだ保持されています。
次に、これらのスレッドを次のように実行する主な方法を定義します。
スレッド[] rs = newスレッド[10]; for(int i = 0; i <10; i ++){rs [i] = newスレッド(new r(i)); } for(thread r:rs){r.start(); } thread.sleep(5000);同期(obj){obj.notifyall(); }10個のスレッドを定義してから、すべてを実行します。待機があるため、10個のスレッドが「起動」を印刷した後、待ちます。次に、メインメソッドはNotifyAllを呼び出します。ここの出力は次のように表示されます。
スレッド - > 0スレッドを待っている - > 4スレッドを待っているスレッド - > 5スレッドを待っているスレッド - > 3スレッド - > 3スレッドを待っている - > 2スレッド - > 1スレッド - > 6スレッド - > 6がスレッドを待っている - > 7スレッド - > 9がスレッド - > 9を待っているのを待っている - > 9
... 30秒以内に他の出力はありません
上記の出力では、待機後、1つのスレッドのみが「実行中」ステートメントを出力し、一定期間(ここで30秒)、他の出力はありません。つまり、他のスレッドは、現在のコードが保持しているロック間に出力されません。
最終的な結論は、待っているスレッドが実行を続けたい場合、2つの条件を満たす必要があります。
他のスレッドは通知またはNotifyAllに通知されており、現在のスレッドに通知されています。
ロックするために他のスレッドと競合した後、ロックのために2つの条件が正常に取得され、どちらも欠落していませんでした。実際、実装レベルでは、NotifyとNotifyAllが同じ効果を達成し、スレッドが実行され続けます。しかし、NotifyAllは免除されます。スレッドが実行されていない後、他のスレッドに通知する必要性が通知されているためです。 notifyを使用し、いつnotifyallを使用するかは、実際の状況に依存します。
上記は、Java NotifyおよびNotifyAllの情報の編集です。今後も関連情報を追加し続けます。このウェブサイトへのご支援ありがとうございます!