スレッドではないオブジェクトでメソッドwait()
とメソッドを呼び出すにはどうすればよいですか? notify()
それは本当に意味がありませんね。
ただし、この 2 つのメソッドはすべての Java オブジェクトで使用できるため、当然のことです。誰かが説明を提供できますか?wait()
と を使用してスレッド間で通信する方法がわかりませんnotify()
。
スレッドではないオブジェクトでメソッドwait()
とメソッドを呼び出すにはどうすればよいですか? notify()
それは本当に意味がありませんね。
ただし、この 2 つのメソッドはすべての Java オブジェクトで使用できるため、当然のことです。誰かが説明を提供できますか?wait()
と を使用してスレッド間で通信する方法がわかりませんnotify()
。
ロックとは、共有データを保護することです。
ロックは、保護されているデータ構造にかかっています。スレッドは、データ構造にアクセスするものです。ロックは、スレッドが安全でない方法でデータ構造にアクセスしないようにするために、データ構造オブジェクトに適用されます。
任意のオブジェクトを固有ロックとして使用できます ( と組み合わせて使用することを意味しますsynchronized
)。このように、共有データにアクセスするメソッドに synchronized 修飾子を追加することで、任意のオブジェクトへのアクセスを保護できます。
wait
およびメソッドは、notify
ロックとして使用されているオブジェクトに対して呼び出されます。ロックは共有通信ポイントです。
ロックを持つスレッドが呼び出すnotifyAll
と、同じロックを待機している他のスレッドに通知されます。ロックを持つスレッドが呼び出すnotify
と、同じロックを待機しているスレッドの 1 つに通知されます。
ロックを持つスレッドが呼び出しwait
を行うと、スレッドはロックを解放し、a) 通知を受信するか、b) 任意にウェイクアップする (「偽のウェイクアップ」) まで休止状態になります。待機中のスレッドは、これら 2 つの理由のいずれかが原因でウェイクアップするまで待機の呼び出しでスタックしたままになります。その場合、スレッドは待機メソッドを終了する前にロックを再取得する必要があります。
保護されたブロックに関する Oracle チュートリアルを参照してください。Drop クラスは共有データ構造であり、Producer および Consumer ランナブルを使用するスレッドがそれにアクセスしています。Drop オブジェクトのロックは、スレッドが Drop オブジェクトのデータにアクセスする方法を制御します。
スレッドは JVM 実装でロックとして使用されるため、アプリケーション開発者はスレッドをロックとして使用しないようにすることをお勧めします。たとえば、Thread.join のドキュメントには次のように書かれています。
この実装は、this.isAlive を条件とする this.wait 呼び出しのループを使用します。スレッドが終了すると、 this.notifyAll メソッドが呼び出されます。アプリケーションが Thread インスタンスで wait、notify、または notifyAll を使用しないことをお勧めします。
Java 5 では、 を実装する明示的なロックが導入されjava.util.concurrent.locks.Lock
ました。これらは暗黙のロックよりも柔軟です。待機と通知 (待機とシグナル) に類似したメソッドがありますが、それらはロックではなく条件にあります。複数の条件を設定すると、特定の種類の通知を待機しているスレッドのみをターゲットにすることができます。
とを使用wait()
しnotify()
てロジックを同期できます。例として
synchronized (lock) {
lock.wait(); // Will block until lock.notify() is called on another thread.
}
// Somewhere else...
...
synchronized (lock) {
lock.notify(); // Will wake up lock.wait()
}
lock
クラスのメンバーとしてObject lock = new Object();
同期ブロック内にコードを配置すると、次のようになります。
sychronized(lock){...}
このブロック内にあるものを実行したいスレッドは、最初にオブジェクトのロックを取得し、一度に 1 つのスレッドだけが同じオブジェクトでロックされたコードを実行できます。どのオブジェクトもロックとして使用できますが、スコープに関連するオブジェクトを慎重に選択する必要があります。たとえば、アカウントに何かを追加する複数のスレッドがあり、それらすべてが次のようなブロック内にそれを担当するコードを持っている場合:
sychronized(this){...}
それらはすべて異なるオブジェクトでロックされているため、同期は行われません。代わりに、アカウント オブジェクトをロックとして使用する必要があります。これらのスレッドには、アカウントからの引き出し方法もあると考えてください。この場合、何かを引き出したいスレッドが空のアカウントに遭遇する状況が発生する可能性があります。デッドロックを回避するために、ある程度の資金が得られるまで待機し、ロックを他のスレッドに解放する必要があります。それが、wait メソッドと notify メソッドの目的です。この例では、空のアカウントに遭遇したスレッドはロックを解放し、入金を行うスレッドからのシグナルを待ちます。
while(balance < amountToWithdraw){
lock.wait();
}
他のスレッドがお金を預けると、他のスレッドが同じロックを待っていることを知らせます。(もちろん、入金と出金を担当するコードは、これが機能し、データの破損を防ぐために、同じロックで同期する必要があります)。
balance += amountToDeposit;
lock.signallAll;
ご覧のとおり、メソッド wait と notify は、同期されたブロックまたはメソッド内でのみ意味があります。
Java では、すべてのオブジェクトがこれら 2 つのメソッドを実装しています。明らかに、モニターがなければ、これら 2 つのメソッドは役に立ちません。
アナロジー: Java スレッドはユーザーであり、トイレはスレッドが実行したいコードのブロックです。Java は、同期化された keywokd を使用して現在コードを実行しているスレッドのコードをロックし、それを使用したい他のスレッドを最初のスレッドが終了するまで待機させる方法を提供します。これらの他のスレッドは待機状態になります。スレッドを待機するためのキューがないため、Java はサービス ステーションほど公平ではありません。要求された順序に関係なく、待機中のスレッドのいずれかが次にモニターを取得する可能性があります。唯一の保証は、遅かれ早かれすべてのスレッドが監視対象のコードを使用できるようになることです。
次の生産者と消費者のコードを見ると:
sharedQueue
オブジェクトはスレッド間のproducer and consumer
スレッド間通信を行います。
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
public class ProducerConsumerSolution {
public static void main(String args[]) {
Vector<Integer> sharedQueue = new Vector<Integer>();
int size = 4;
Thread prodThread = new Thread(new Producer(sharedQueue, size), "Producer");
Thread consThread = new Thread(new Consumer(sharedQueue, size), "Consumer");
prodThread.start();
consThread.start();
}
}
class Producer implements Runnable {
private final Vector<Integer> sharedQueue;
private final int SIZE;
public Producer(Vector<Integer> sharedQueue, int size) {
this.sharedQueue = sharedQueue;
this.SIZE = size;
}
@Override
public void run() {
for (int i = 0; i < 7; i++) {
System.out.println("Produced: " + i);
try {
produce(i);
} catch (InterruptedException ex) {
Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private void produce(int i) throws InterruptedException {
// wait if queue is full
while (sharedQueue.size() == SIZE) {
synchronized (sharedQueue) {
System.out.println("Queue is full " + Thread.currentThread().getName() + " is waiting , size: "
+ sharedQueue.size());
sharedQueue.wait();
}
}
// producing element and notify consumers
synchronized (sharedQueue) {
sharedQueue.add(i);
sharedQueue.notifyAll();
}
}
}
class Consumer implements Runnable {
private final Vector<Integer> sharedQueue;
private final int SIZE;
public Consumer(Vector<Integer> sharedQueue, int size) {
this.sharedQueue = sharedQueue;
this.SIZE = size;
}
@Override
public void run() {
while (true) {
try {
System.out.println("Consumed: " + consume());
Thread.sleep(50);
} catch (InterruptedException ex) {
Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
private int consume() throws InterruptedException {
//wait if queue is empty
while (sharedQueue.isEmpty()) {
synchronized (sharedQueue) {
System.out.println("Queue is empty " + Thread.currentThread().getName()
+ " is waiting , size: " + sharedQueue.size());
sharedQueue.wait();
}
}
//Otherwise consume element and notify waiting producer
synchronized (sharedQueue) {
sharedQueue.notifyAll();
return (Integer) sharedQueue.remove(0);
}
}
}
「このメソッドは、このオブジェクトのモニターの所有者であるスレッドによってのみ呼び出されるべきです。」したがって、オブジェクトのモニターであるスレッドがあることを確認する必要があると思います。