スレッドではないオブジェクトでwait()
およびnotify()
メソッドをどのように呼び出すことができますか?それは本当に理にかなっていないのですか?
ただし、2つのメソッドはすべてのJavaオブジェクトで使用可能です。誰かが説明を提供できますか?wait()
およびnotify()
。
ロックとは、共有データを保護することです。
ロックは、保護されているデータ構造にあります。スレッドは、データ構造にアクセスするものです。ロックは、スレッドが安全でない方法でデータ構造にアクセスするのを防ぐために、データ構造オブジェクトにあります。
任意のオブジェクトを組み込みロックとして使用できます(synchronized
と組み合わせて使用することを意味します)。この方法では、共有データにアクセスするメソッドにsynchronized修飾子を追加することにより、オブジェクトへのアクセスを保護できます。
wait
およびnotify
メソッドは、ロックとして使用されているオブジェクトで呼び出されます。ロックは共有通信ポイントです。
ロックを持つスレッドがnotifyAll
を呼び出すと、その同じロックを待機している他のスレッドに通知されます。ロックを持つスレッドがnotify
を呼び出すと、その同じロックを待機しているスレッドの1つが通知を受けます。
ロックのあるスレッドがwait
を呼び出すと、スレッドはロックを解除し、a)通知を受信するか、b)任意に起動する(「スプリアスウェイクアップ」)まで休止します。これら2つの理由のいずれかが原因でウェイクアップするまで、待機中のスレッドは呼び出しでスタックしたままになり、スレッドはwaitメソッドを終了する前にロックを再取得する必要があります。
保護されたブロックに関するOracleチュートリアル を参照してください。Dropクラスは共有データ構造であり、ProducerおよびConsumerの実行可能ファイルを使用するスレッドがアクセスします。 Dropオブジェクトのロックは、スレッドがDropオブジェクトのデータにアクセスする方法を制御します。
スレッドはJVM実装でロックとして使用されるため、アプリケーション開発者はスレッドをロックとして使用しないようにしてください。たとえば、 Thread.joinのドキュメント は次のとおりです。
この実装は、this.isAliveを条件とするthis.wait呼び出しのループを使用します。スレッドが終了すると、this.notifyAllメソッドが呼び出されます。アプリケーションは、スレッドインスタンスで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();
である
静的Thread
クラスメソッドsleep()
を使用して、必要に応じてスレッドを一時停止できます。
public class Main {
//some code here
//Thre thread will sleep for 5sec.
Thread.sleep(5000);
}
一部のオブジェクトを停止する場合は、syncronized
ブロック内でこのメソッドを呼び出す必要があります。
public class Main {
//some code
public void waitObject(Object object) throws InterruptedException {
synchronized(object) {
object.wait();
}
}
public void notifyObject(Object object) throws InterruptedException {
synchronized(object) {
object.notify();
}
}
}
追伸あなたの質問を間違って理解した場合、私は腹を立てます(英語は私のネイティブではありません)
洗面所という実際の例を使用して考えてください。オフィスで洗面所を使用する場合、使用後に洗面所に誰も来ないようにするための2つのオプションがあります。
どのオプションを選択しますか?
はい、Javalandでも同じです。
したがって、上記の話では、
したがって、実際の生活と同じように、プライベートなビジネスがあるときには、そのオブジェクトをロックします。そして、そのオブジェクトを使い終わったら、ロックを解除します!.
(はいはい、これは何が起こるかについての非常に簡単な説明です。もちろん実際の概念はこれとわずかに異なりますが、これは出発点です)
同期ブロック内にコードを配置する場合:
sychronized(lock){...}
このブロック内にあるものを何でも実行しようとするスレッドは、最初にオブジェクトのロックを取得し、一度に1つのスレッドのみが同じオブジェクトでロックされたコードを実行できます。任意のオブジェクトをロックとして使用できますが、スコープに関連するオブジェクトを選択するよう注意する必要があります。たとえば、アカウントに何かを追加する複数のスレッドがあり、すべてのスレッドが次のようなブロック内でそれを担当するコードを持っている場合:
sychronized(this){...}
それらはすべて異なるオブジェクトでロックされているため、同期は行われません。代わりに、アカウントオブジェクトをロックとして使用する必要があります。ここで、これらのスレッドにはアカウントから引き出す方法もあると考えてください。この場合、何かを撤回しようとするスレッドが空のアカウントに遭遇する状況が発生する可能性があります。デッドロックを避けるために、いくらかのお金が出るまで待機し、ロックを他のスレッドに解放する必要があります。それがwaitメソッドとnotifyメソッドの目的です。この例では、空のアカウントに遭遇したスレッドはロックを解除し、入金を行うスレッドからのシグナルを待ちます:
while(balance < amountToWithdraw){
lock.wait();
}
他のスレッドがいくらかのお金を預けると、同じロックで待機している他のスレッドに信号を送ります。 (もちろん、入金と出金を行うコードは、これが機能し、データ破損を防ぐために同じロックで同期する必要があります)。
balance += amountToDeposit;
lock.signallAll;
ご覧のように、メソッドは待機し、通知は同期ブロックまたはメソッド内でのみ意味があります。
Javaすべてのオブジェクトはこれら2つのメソッドを実装しています。明らかにモニターがない場合、これらの2つのメソッドは役に立ちません。
実際には、wait
、notify
メンバー関数はスレッドに属しているべきではなく、名前に属するべきものcondition variableposix thread に由来します。また、cppがこの概念をどのようにラップするか、専用クラス名 std :: condition_variable にラップする方法を見ることができます。
CppはJavaよりもカプセル化の方が優れていると思います。Javaこれをやりすぎると、概念が直接Objectクラスに入れられ、最初は人々を混乱させます。
Analogy: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);
}
}
}
「このメソッドは、このオブジェクトのモニターの所有者であるスレッドによってのみ呼び出される必要があります。」したがって、オブジェクトのモニターであるスレッドがあることを確認する必要があると思います。