1.同期して使用する必要がある理由
Javaでは、wait()とnotify()はObjectのメンバー関数であり、基盤の基盤です。Javaがwait()とnotiry()を、Threadクラスのメンバー関数や他のクラスの関数としてではなく、このような基本クラスに配置するのはなぜですか?
この質問に答える前に、wait()とnotify()を同期して使用する必要がある理由について説明しましょう。次のコードを見てください。
public class SynchronizedDemo3 {
private Object lock = new Object();
public void increment() {
synchronized (lock) {
// do something
lock.wait();
// do something
}
}
public void decrement() {
synchronized (lock) {
// do something
lock.notify();
// do something
}
}
}
2つのスレッドAとBを開始します。スレッドAはincrement()メソッドを呼び出し、スレッドBはdecrement()メソッドを呼び出します。明らかに、2つのスレッドが通信する必要があります。同じオブジェクトに対して、1つのスレッドがオブジェクトのwait()メソッドを呼び出し、別のスレッドがオブジェクトのnotify()メソッドを呼び出す場合、オブジェクト自体は相互に排他的である必要があります。すべて、wait()メソッドとnotify()メソッドを呼び出す前に、synchronizedキーワードを使用してオブジェクトロックをロックする必要があります。
同期キーワードは任意のオブジェクトのメンバー関数に追加でき、任意のオブジェクトがロックになる可能性があることは誰もが知っています。次に、wait()とnotify()が非常に人気があり、Objectはすべてのクラスの基本クラスであるため、wait()とnotify()がObjectの最適な場所です。
2. wait()のときにロックを解除する必要があるのはなぜですか
上記のコードによると、スレッドAが同期(ロック)コードブロックに入ると、つまりロックがロックされます。このとき、wait()を呼び出すとブロック状態になり、同期コードブロックを終了できなくなります。スレッドBは次のことができます。同期を入力しない(ロック)コードブロックでは、notify()を呼び出す機会はありません。デッドロックではないでしょうか。
これには重要な問題が含まれます。wait()内では、最初にロックを解除してからブロック状態に入る必要があります。その後、スレッドBは再びロックを保持し、notify()メソッドを呼び出してスレッドをウェイクアップできます。 A、そしてスレッドAは再びロックを取得し、後続のコードロジックの実行を継続し、実行後に同期されたコードブロックを終了し、ロックを再度解放します。
この方法でのみ、デッドロックの可能性を回避できます。
3. wait()とnotify()の問題
notify()は一度に1つのスレッドしかウェイクアップできないためです。たとえば、マルチプロデューサー-マルチコンシューマーモードでは、プロデューサーがコンシューマーをウェイクアップしたいが、プロデューサーをウェイクアップする可能性があり、コンシューマーが望んでいる場合生産者を目覚めさせるために、消費者を目覚めさせるかもしれないこともあります。notifyAll()を使用して、wait()中にすべてのスレッドをウェイクアップし、ロックを取得することをお勧めします。JUC同時実行パッケージのロックと条件の組み合わせは、この問題を完全に解決できます。
4.まとめ
この記事では主に、wait()、notify()、notifyAll()メソッドがObjectクラスにある理由と、wait()メソッドが呼び出された後にロックを解除する必要がある理由を紹介します。同期されたキーワードロックの本質と、スレッドの相互排除と同期の原則を習得している限り、これらを理解することは難しくありません。