Object
クラスにwait()
を配置する背後にある概念を理解するのに苦労しています。この質問のために、wait()
とnotifyAll()
がThread
クラスにあるかどうかを考慮してください。
class Reader extends Thread {
Calculator c;
public Reader(Calculator calc) {
c = calc;
}
public void run() {
synchronized(c) { //line 9
try {
System.out.println("Waiting for calculation...");
c.wait();
} catch (InterruptedException e) {}
System.out.println("Total is: " + c.total);
}
}
public static void main(String [] args) {
Calculator calculator = new Calculator();
new Reader(calculator).start();
new Reader(calculator).start();
new Reader(calculator).start();
calculator.start();
}
}
class Calculator extends Thread {
int total;
public void run() {
synchronized(this) { //Line 31
for(int i=0;i<100;i++) {
total += i;
}
notifyAll();
}
}
}
私の質問は、それがどのような違いをもたらしたのかということです。 9行目では、オブジェクトcのロックを取得してから、待機を使用する前にオブジェクトのロックを取得する必要があるという待機条件を満たす待機を実行しています。 。
私はオブジェクトクラスにwait()を置くことの概念を理解するのに苦労しているだけですこの質問のために、wait()とnotifyAll()がスレッドクラスにあると考えてください
Java言語)では、Object
の特定のインスタンスでwait()
–正確にそのオブジェクトに割り当てられたモニターを送信します。その特定のオブジェクトインスタンスを待機している1つのスレッドへのシグナルは、そのオブジェクトでnotify()
を呼び出します。そのオブジェクトインスタンスを待機しているすべてのスレッドにシグナルを送信する場合は、notifyAll()
そのオブジェクト。
wait()
とnotify()
が代わりにThread
にある場合、各スレッドは他のすべてのスレッドのステータスを知る必要があります。 thread1は、thread2が特定のリソースへのアクセスを待機していることをどのようにして知るのでしょうか? thread1がthread2.notify()
を呼び出す必要がある場合、何らかの方法でthread2
待っていました。スレッドが必要なリソースまたはアクションを登録するための何らかのメカニズムが必要になります。これにより、他のものが準備ができたり利用可能になったときにそれらを通知できるようになります。
Javaでは、オブジェクト自体はスレッド間で共有されるエンティティであり、スレッドが相互に通信できるようにします。スレッドは互いに特定の知識を持たず、非同期で実行できます。これらは実行され、ロック、待機、およびアクセスを取得することをobjectで通知します。他のスレッドの知識はなく、ステータスを知る必要はありません。リソースを待機しているのはスレッド2であることを知る必要はありません。リソースについて通知するだけで、待機している人(もしあれば)に通知されます。
Javaでは、オブジェクトを同期、ミューテックス、およびスレッド間の通信ポイントとして使用します。重要なコードブロックへのミューテックスアクセスを取得し、メモリを同期するために、オブジェクトを同期します。何らかの条件が変更されるのを待っている場合は、オブジェクトを待ちます–何らかのリソースが利用可能になるのです。スリープ状態のスレッドを呼び起こす場合は、オブジェクトで通知します。
// locks should be final objects so the object instance we are synchronizing on,
// never changes
private final Object lock = new Object();
...
// ensure that the thread has a mutex lock on some key code
synchronized (lock) {
...
// i need to wait for other threads to finish with some resource
// this releases the lock and waits on the associated monitor
lock.wait();
...
// i need to signal another thread that some state has changed and they can
// awake and continue to run
lock.notify();
}
プログラムには、任意の数のロックオブジェクトを含めることができます。それぞれが特定のリソースまたはコードセグメントをロックします。 100個のロックオブジェクトと4個のスレッドのみがある場合があります。スレッドがプログラムのさまざまな部分を実行すると、ロックオブジェクトの1つに排他的にアクセスできます。繰り返しますが、他のスレッドの実行状態を知る必要はありません。
これにより、ソフトウェアで実行しているスレッドの数を必要なだけ拡大または縮小できます。 4つのスレッドが外部リソースをブロックしすぎていることがわかった場合は、その数を増やすことができます。ボロボロになったサーバーを押し込みすぎると、実行中のスレッドの数が減ります。ロックオブジェクトは、実行中のスレッドの数に関係なく、スレッド間のミューテックスと通信を保証します。
Wait()メソッドとnotify()メソッドがObjectクラスに属する理由をよりよく理解するために、実際の例を挙げます。ガソリンスタンドに1つのトイレがあり、そのキーはサービスデスクに保持されているとします。トイレは、ドライバーを追い越すための共有リソースです。この共有リソースを使用するには、将来のユーザーがトイレの鍵の鍵を取得する必要があります。ユーザーはサービスデスクに行き、キーを取得し、ドアを開け、内部からロックし、施設を使用します。
一方、2人目の見込みユーザーがガソリンスタンドに到着すると、トイレがロックされているため、利用できません。彼はサービスデスクに行きますが、キーは現在のユーザーの手にあるため、そこにはありません。現在のユーザーが終了すると、ドアのロックを解除し、サービスデスクにキーを返します。彼は待っている顧客を気にしません。サービスデスクは、待機中の顧客にキーを提供します。トイレがロックされている間に複数の見込みユーザーが現れた場合、ロックの鍵を待つキューを形成する必要があります。各スレッドには、誰がトイレにいるのかわかりません。
この類推をJavaに適用すると、Javaスレッドはユーザーであり、トイレはスレッドが実行したいコードのブロックです。Javaはsynchronizedキーワードを使用して現在実行中のスレッドのコードをロックし、それを使用する他のスレッドを最初のスレッドが終了するまで待機させる方法。これらの他のスレッドは待機状態になります。Javaは、待機スレッドのキューがないため、サービスステーションほど公平ではありません。待機しているスレッドのいずれかは、要求された順序に関係なく、次にモニターを取得できます。監視されたコードを遅かれ早かれ使用できるようになります。
最後に、あなたの質問への答え:ロックはキーオブジェクトまたはサービスデスクである可能性があります。どれもスレッドではありません。
ただし、これらは、トイレがロックされているか開いているかを現在決定するオブジェクトです。これらは、バスルームが開いていることを通知する(「通知する」)か、ロックされているときに待機するように人々に依頼することができる位置にあるオブジェクトです。
この質問に対する他の答えはすべて、Javaにはeveryオブジェクトに関連付けられた1つのミューテックスがあるという重要な点を見逃しています。 (私はあなたがミューテックスまたは「ロック」が何であるかを知っていると仮定しています。)これはnotの概念を持つほとんどのプログラミング言語の場合ですロック」。たとえば、Rubyでは、必要な数のMutex
オブジェクトを明示的に作成する必要があります。
Java=の作成者がこの選択をした理由を知っていると思います(ただし、私の意見では、それは間違いでした)。理由はsynchronized
の包含に関係しています。キーワード:Java(単純に)の作成者は、synchronized
メソッドを言語に含めることで、人々が正しいマルチスレッドコードを簡単に記述できるようになると考えていたと思います。すべての共有状態をオブジェクトにカプセル化し、その状態にアクセスするメソッドをsynchronized
として宣言すれば完了です!しかし、それはうまくいきませんでした...
とにかく、どのクラスにもsynchronized
メソッドを含めることができるため、オブジェクトごとに1つのmutexが必要です。これは、synchronized
メソッドがロックおよびロック解除できます。
wait
とnotify
は両方ともミューテックスに依存しています。多分あなたはすでにこれがなぜそうなのか理解しています...もしそうでなければ、私はさらに説明を追加することができますが、今のところ、両方の方法がミューテックスで動作する必要があると言ってみましょう。各Javaオブジェクトにはミューテックスがあるため、wait
およびnotify
を任意のJavaオブジェクトで呼び出すことができます。つまり、Object
のメソッドとして宣言する必要があります。
別のオプションは、Thread
または何かに静的メソッドを配置することでした。このメソッドは、Object
を引数として受け取ります。新しいJavaプログラマーにとってはそれほど混乱しませんでした。しかし、彼らはそのようにはしませんでした。これらの決定を変更するには遅すぎます。
最初の質問への答えは、Javaにあるすべてのオブジェクトにはlock(monitor)
とwait(),notify(),notifyAll()
が1つしかないため、Object
クラスの一部である理由をモニターの共有に使用しますThread
classではなく。
簡単に言えば、その理由は次のとおりです。
Object
にはモニターがあります。Object
にアクセスできます。 synchronized
メソッド/ブロックのオブジェクトモニターを同時に保持できるスレッドは1つだけです。wait(), notify() and notifyAll()
メソッドはObject
クラスにあるため、そのobject
で作成されたすべてのスレッドが他のスレッドと通信できます。synchronized or Lock
_ APIを使用)と通信(wait() and notify()
)は2つの異なる概念です。Thread
クラスにwait(), notify() and notifyAll()
メソッドが含まれている場合、以下の問題が発生します。
Thread
通信の問題Synchronization
は使用できません。各スレッドにモニターがある場合、同期を達成する方法はありませんInconsistency
オブジェクトの状態詳細については、これを参照してください 記事 .
待機および通知操作は暗黙ロックで機能し、暗黙ロックはスレッド間通信を可能にするものです。そして、すべてのオブジェクトは暗黙のオブジェクトの独自のコピーを持っています。そのため、待機を続けて、暗黙的なロックがどこにあるかを通知することは良い判断です。
あるいは、待機して通知することは、Threadクラスにも存在している可能性があります。 wait()の代わりに、notifyと同じThread.getCurrentThread()。wait()を呼び出さなければならない場合があります。待機操作と通知操作には2つの必須パラメーターがあります。1つは待機するスレッド、もう1つはオブジェクトの暗黙的なロックです。どちらも、オブジェクトおよびスレッドクラスで使用できます。 Threadクラスのwait()メソッドは、Objectクラスの場合と同じことを行い、現在のスレッドを最後に取得したロックの待機状態待機に移行します。
確かに、待機と通知はThreadクラスにも存在する可能性がありますが、オブジェクトクラスに保持するという設計上の決定に似ています。
これらのメソッドはロックに対して機能し、ロックはスレッドではなくオブジェクトに関連付けられます。したがって、Objectクラスにあります。
メソッドwait()、notify()、およびnotifyAll()は単なるメソッドであるだけでなく、これらは同期ユーティリティであり、Javaのスレッド間の通信メカニズムで使用されます。
詳細な説明については、http://parameshk.blogspot.in/2013/11/why-wait-notify-and-をご覧ください。 notifyall-methods.html
これはこの質問に対する私の2セントです...これが完全に当てはまるかどうかはわかりません。
各オブジェクトにはモニターとwaitset->スレッドのセットがあります(これはおそらくOSレベルでの詳細です)。これは、モニターとwaitsetがオブジェクトのプライベートメンバーとして見えることを意味します。 Threadクラスにwait()およびnotify()メソッドがあると、waitsetへのパブリックアクセスを許可するか、get-setメソッドを使用してwaitsetを変更することになります。それは悪い設計なので、あなたはそれをしたくないでしょう。
オブジェクトがモニターを待機しているスレッドを認識していることを考えると、スレッドクラスのオブジェクトがそれらのそれぞれを行って目覚めるのではなく、オブジェクトを待ってそれらのスレッドを目覚めさせるのはオブジェクトの仕事でなければなりませんスレッドクラスオブジェクトがwaitsetへのアクセスを許可されている場合のみ可能です。ただし、特定のスレッドが待機中の各スレッドを呼び出して起動するのは仕事ではありません。 (これらのメソッドがすべてThreadクラス内にある場合、これはまさに発生することです)。その仕事は、ロックを解除し、独自のタスクを進めることです。スレッドは独立して動作し、他のスレッドがオブジェクトモニターを待機していることを知る必要はありません(スレッドクラスオブジェクトの不必要な詳細です)。各スレッドがそれ自体で目覚め始めた場合、それはそのコア機能から離れて移動しており、それは独自のタスクを実行することです。数千のスレッドが存在する可能性のあるシーンを考えると、パフォーマンスにどの程度の影響があるかを想定できます。したがって、Object Classは誰が待機しているかを知っているので、待機中のスレッドを呼び起こすジョブを実行でき、notify()を送信したスレッドはそれ以降の処理を実行できます。
類推(おそらく正しいものではないが、他のことは考えられない)停電が発生した場合、その会社の顧客担当者に電話をかけます。彼女は、それを修正するために連絡する適切な人を知っているからです。消費者としてのあなたは、その背後にいるエンジニアが誰であるかを知ることは許されておらず、たとえ知っていたとしても、それぞれに電話してトラブルを伝えることはできません(それは義務ではありません。停止とCRの仕事は、適切なエンジニアに行って通知することです。
これが正しいかどうかを教えてください...(私は時々私の言葉と混同する能力があります)。
wait-waitメソッドは、現在のスレッドにモニターを放棄してスリープ状態になるように指示します。
notify-このオブジェクトのモニターで待機している単一のスレッドを起こします。
したがって、wait()およびnotify()メソッドがモニターレベルで機能するのがわかります。現在モニターを保持しているスレッドは、wait()メソッドと、オブジェクトのモニターは、スレッドが起動できることを通知されます。
ここで注意すべき重要な点は、モニターが特定のスレッドではなくオブジェクトに割り当てられることです。これが、これらのメソッドがObjectクラスにある理由の1つです。スレッドを繰り返すには、オブジェクトのモニターで待機(ロック)し、オブジェクトでnotify()を呼び出して、オブジェクトのモニターで待機しているスレッドを起動します。