この章では、スレッド待機/ウェイクアップ方法を紹介します。関係するコンテンツには以下が含まれます。
1。WAIT()、NOTIFY()、NOTIFYALL()、およびその他のメソッドの紹介
2。wait()とnotify()
3。待機(長いタイムアウト)とNotify()
4。Wait()およびNotifyAll()
5。notify()、wait()、およびスレッドではなくオブジェクトで定義されている他の関数がある理由
wait()、notify()、notifyall()、およびその他のメソッドの紹介
object.javaでは、wait()、notify()、notifyall()などのインターフェイスが定義されています。 wait()の関数は、現在のスレッドが待機状態に入るようにすることです。また、wait()は、現在のスレッドが保持されるロックを解放します。 Notify()およびNotifyAll()の役割は、現在のオブジェクトの待機中のスレッドを目覚めさせることです。
オブジェクトクラスの待機/覚醒に関するAPIの詳細は次のとおりです。
notify() - このオブジェクトモニターを待っている単一のスレッドを起動します。
notifyall() - このオブジェクトモニターで待機しているすべてのスレッドを起動します。
wait() - 現在のスレッドを「wait(blocking)状態」に入れ、「他のスレッドがこのオブジェクトのnotify()メソッドまたはnotifyall()メソッドを呼び出すまで」、現在のスレッドは目覚めます(」準備ができている」)。
wait(long timeout) - 現在のスレッドを「待機(ブロック)状態」にし、「他のスレッドがこのオブジェクトのnotify()メソッドまたはnotifyall()メソッドを呼び出すか、指定された時間を超えるまで」にします。そして、現在のスレッドが目覚めます(「準備ができている」を入力)。
wait(long timeout、int nanos) - 別のスレッドがこのオブジェクトのnotify()メソッドまたはnotifyall()メソッドを呼び出すまで、または他のスレッドが現在のスレッドを中断するまで、現在のスレッドを「待機(ブロック)状態」に入れます。または、実際の時間が超えています」、そして現在のスレッドが目覚めます(「Ready State」に入力されます)。
2。wait()and Notify()の例
以下は、「wait()and Notify()一緒に」を示す例です。
コードコピーは次のとおりです。
// waittest.javaソースコード
クラスのthreadaはスレッドを拡張します{
public threada(string name){
super(name);
}
public void run(){
同期(This){
system.out.println(thread.currentthread()。getname()+"call notify()");
//現在の待機スレッドを起動します
notify();
}
}
}
パブリッククラスwaittest {
public static void main(string [] args){
threada t1 = new Streada( "T1");
同期(T1){
試す {
//「スレッドT1」を開始
system.out.println(thread.currentthread()。getname()+"start t1");
t1.start();
//メインスレッドは、T1がnotify()を介して目を覚ますのを待ちます。
system.out.println(thread.currentthread()。getname()+"wait()");
t1.wait();
system.out.println(thread.currentthread()。getname()+"continue");
} catch(arternedexception e){
e.printstacktrace();
}
}
}
}
実行結果:
コードコピーは次のとおりです。
メインスタートT1
メインウェイト()
t1 call notify()
メイン続行
結果説明:
次の図は、「メインスレッド」と「スレッドT1」の流れを示しています。
(01)図の「メインスレッド」は「メインスレッドメイン」を表すことに注意してください。 「スレッドT1」は、「スレッドT1」がWaittestで開始されることを表します。 「ロック」は、「オブジェクトT1の同期ロック」を表します。
(02)「メインスレッド」は、新しいスレッド(「T1」)を介して新しい「スレッドT1」を作成します。次に、「T1オブジェクトの同期ロック」は、同期(T1)によって取得されます。次に、t1.start()を呼び出して「スレッドT1」を開始します。
(03)「メインスレッド」はt1.wait()を実行して「T1オブジェクトのロック」をリリースし、「待機(ブロッキング)状態」に入ります。 T1オブジェクトのスレッドがnotify()またはnotifyall()を介して目覚めるのを待ちます。
(04)「スレッドT1」が実行された後、「現在のオブジェクトのロック」が同期して取得されます(this)。 「メインスレッド」。
(05)「スレッドT1」が完了した後、「現在のオブジェクトのロック」を解放します。その後すぐに、「メインスレッド」は「T1オブジェクトのロック」を取得し、実行します。
上記のコードは?かつて尋ねた友人:t1.wait()は「スレッドT1」を待つ必要があります。
この質問に答える前に、JDKドキュメントの待機に関する段落を見てみましょう。
コードコピーは次のとおりです。
このオブジェクトの別のスレッドがnotify()メソッドまたはnotifyall()メソッドを呼び出すまで、現在のスレッドが待機します。
言い換えれば、この方法は、単にコールウェイ(0)を実行するように正確に動作します。
現在のスレッドは、このオブジェクトのモニターの所有権をリリースし、別のスレッドがこのオブジェクトのモニターが通話を待っているか、通知を通知するか、通知メソッドを待っていますモニターの所有権を再検討し、実行を再開できます。
中国語の意味は大まかです:
「現在のスレッド」は、別のスレッドがnotify()またはnotifyall()を呼び出してスレッドを覚醒させるまで待機します。言い換えれば、この方法はwait(0)と同じ効果を持っています! (補足、待機(長いミリ)メソッドの場合、ミリスが0の場合、notify()またはnotifyall()によって目が覚めるまで無限に待つことを意味します)。
「現在のスレッド」がwait()を呼び出す場合、オブジェクトの同期ロックが必要です。スレッドがwait()を呼び出した後、ロックは「他のスレッド」がオブジェクトの同期ロックのnotify()またはnotifyall()メソッドを呼び出すまで待ちます。その後、スレッドは「このオブジェクトの同期ロック」を再取得するまで待ち続け、その後実行し続けることができます。
注:JDKの説明では、wait()の関数は「現在のスレッド」をwaitにすることであり、「現在のスレッド」はCPUで実行されているスレッドを指します!
これはまた、t1.wait()は「thread t1」を介して呼び出されるwait()メソッドですが、t1.wait()が呼び出される場所は「メインスレッドメイン」にあります。メインスレッドは、T1.wait()を実行する前の「現在のスレッド」、つまり実行状態でなければなりません。したがって、現時点での「現在のスレッド」は「メインスレッドメイン」です!したがって、t1.wait()は、「スレッドT1」ではなく「メインスレッド」を待つようにすることです!
3。待機(長いタイムアウト)とNotify()
待機(長いタイムアウト)は、現在のスレッドを「待機(ブロッキング)状態」に入れ、「他のスレッドがこのオブジェクトのnotify()メソッドまたはnotifyall()メソッドを呼び出すか、指定された時間を超えるまで」、および現在のスレッドは目覚め(入力)「準備ができています」)。
次の例は、待機(長いタイムアウト)タイムアウトを示しており、スレッドは目覚めています。
コードコピーは次のとおりです。
// waittimeouttest.javaのソースコード
クラスのthreadaはスレッドを拡張します{
public threada(string name){
super(name);
}
public void run(){
system.out.println(thread.currentthread()。getname() + "run");
//継続的に実行される悪循環。
while(true)
}
}
パブリッククラスwaittimeouttest {
public static void main(string [] args){
threada t1 = new Streada( "T1");
同期(T1){
試す {
//「スレッドT1」を開始
system.out.println(thread.currentthread()。getname() + "start t1");
t1.start();
//メインスレッドは、T1がnotify()またはnotifyall()を介して目を覚ますか、3000msを超える遅延を待ちます。
system.out.println(thread.currentthread()。getname() + "call wait");
T1.Wait(3000);
system.out.println(thread.currentthread()。getname() + "continue");
} catch(arternedexception e){
e.printstacktrace();
}
}
}
}
実行結果:
コードコピーは次のとおりです。
メインスタートT1
メインコール待ち
T1 run //約3秒後...出力「Main Continue」
メイン続行
結果説明:
次の図は、「メインスレッド」と「スレッドT1」の流れを示しています。
(01)図の「メインスレッド」は、waittimeouttestメインスレッド(つまり、スレッドメイン)を表していることに注意してください。 「Thread T1」は、Waittestで開始されたスレッドT1を表します。 「ロック」は、「オブジェクトT1の同期ロック」を表します。
(02)メインスレッドメインはt1.start()を実行して「スレッドT1」を開始します。
(03)メインスレッドメインはT1.Wait(3000)を実行し、この時点でメインスレッドは「ブロッキング状態」に入ります。 「T1オブジェクトロックに使用されるスレッドがnotify()またはnotifyall()または「3000msタイムアウト後」を介して目を覚ます必要があります。メインスレッドメインは「準備状態」に入り、実行できます。
(04)「スレッドT1」が実行された後、デッドループに入り、実行を続けます。
(05)タイムアウトが3000MSの後、メインスレッドメインが「Ready State」に入り、「実行中の状態」に入ります。
4。Wait()およびNotifyAll()
前の例を介して、notify()がこのオブジェクトモニターを待っている単一のスレッドを起動できることがわかっています。
以下では、その機能を介してnotifyall()の使用法を示します。
コードコピーは次のとおりです。
パブリッククラスNotifyAlltest {
private staticオブジェクトobj = new object();
public static void main(string [] args){
threada t1 = new Streada( "T1");
threada t2 = new Streada( "T2");
threada t3 = new Streada( "T3");
t1.start();
t2.start();
t3.Start();
試す {
system.out.println(thread.currentthread()。getName()+"sleep(3000)");
thread.sleep(3000);
} catch(arternedexception e){
e.printstacktrace();
}
同期(obj){
//メインスレッドがウェイクアップを待っています。
system.out.println(thread.currentthread()。getname()+"notifyall()");
obj.notifyall();
}
}
静的クラススレッドはスレッドを拡張します{
public threada(string name){
super(name);
}
public void run(){
同期(obj){
試す {
//出力結果を印刷します
system.out.println(thread.currentthread()。getName() + "wait");
//現在の待機スレッドを起動します
obj.wait();
//出力結果を印刷します
system.out.println(thread.currentthread()。getname() + "continue");
} catch(arternedexception e){
e.printstacktrace();
}
}
}
}
}
実行結果:
コードコピーは次のとおりです。
T1待ってください
メインスリープ(3000)
T3待ってください
T2待ってください
main notifyall()
T2続行します
T3続行します
T1続行します
結果説明:
以下のフローチャートを参照してください。
(01)3スレッド「T1」、「T2」、および「T3」が作成され、メインスレッドで開始されました。
(02)メインスレッドは、睡眠中に3秒間眠ります(3000)。メインスレッドの睡眠中3秒間、3つのスレッド「T1」、「T2」、および「T3」がすべて実行されていると仮定します。 「T1」を実行すると、OBJ.WAIT()を実行して、他のスレッドがnotify()またはnofityall()を介して目覚めます。また、他のスレッドがnofity()またはnofityall()を介してそれらを起こすのを待ちます。
(03)メインスレッドは3秒間眠り、その後実行されます。 obj.notifyall()を実行して、OBJの待機スレッドを目覚めさせます。つまり、3つのスレッド「T1」、「T2」、「T3」を覚ます。 メインスレッドの同期(OBJ)が実行された直後、メインスレッドは「OBJロック」を解放します。このようにして、「T1」、「T2」、および「T3」は「OBJロック」を取得して実行し続けることができます!
5。notify()、wait()、およびスレッドではなくオブジェクトで定義されている他の関数がある理由
Synchronizedのようなオブジェクトのwait()、notify()などの関数は、「オブジェクト同期ロック」で動作します。
wait()は「現在のスレッド」を待機します。スレッドはロックによって保持されている「同期ロック」をリリースする必要があります。実行できません!
OK、スレッドがwait()を呼び出した後、そのロックによって保持されている「同期ロック」がリリースされます。さて、質問について考えてください:待機中のスレッドの目覚めに基づいてNotify()は何ですか?または、wait()とnotify()の間の相関関係は何ですか?答えは、「オブジェクト同期ロック」に基づいています。
待っているスレッドを目覚めさせるスレッド(「ウェイクアップスレッド」と呼ばれます)、「オブジェクトの同期ロック」(ここでの同期ロックは、待機スレッドの同期ロックと同じでなければなりません)のみを取得します)通話notify()またはnotifyall()メソッドの後、待機スレッドを目覚めることができます。ただし、待機中のスレッドは目覚めていますが、ウェイクアップスレッドは「オブジェクトの同期ロック」を保持しているため、すぐに実行できません。 「オブジェクトの同期ロック」を取得して実行し続ける前に、ウェイクアップスレッドが「オブジェクトの同期ロック」を解放するまで待つ必要があります。
要するに、notify()、wait()は、オブジェクトロックによって保持されている「同期ロック」に依存しており、各オブジェクトは1つだけです!これが、notify()、wait()などの関数がスレッドクラスではなくオブジェクトクラスで定義される理由です。