Java では、スレッド間通信は次のようにして実現できます。
1. 共有変数
スレッドは共有変数を介して通信できます。複数のスレッドが同じ変数を読み書きして情報を交換できます。この場合、データ競合や一貫性のない結果を避けるために、共有変数へのスレッド アクセスが同期されていることを確認する必要があります。
以下は、共有変数を使用したスレッド通信のサンプル コードです。
class Message {
private String content;
private boolean hasNewMessage = false;
public synchronized void putMessage(String content) {
while (hasNewMessage) {
try {
wait(); // 等待直到消息被消费
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
this.content = content;
hasNewMessage = true;
notifyAll(); // 唤醒等待的线程
}
public synchronized String getMessage() {
while (!hasNewMessage) {
try {
wait(); // 等待直到有新消息
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
hasNewMessage = false;
notifyAll(); // 唤醒等待的线程
return content;
}
}
class Producer implements Runnable {
private Message message;
public Producer(Message message) {
this.message = message;
}
public void run() {
String[] messages = {
"Hello", "World", "Goodbye" };
for (String msg : messages) {
message.putMessage(msg);
System.out.println("Producer: " + msg);
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
message.putMessage("Done");
}
}
class Consumer implements Runnable {
private Message message;
public Consumer(Message message) {
this.message = message;
}
public void run() {
String msg = "";
while (!msg.equals("Done")) {
msg = message.getMessage();
System.out.println("Consumer: " + msg);
}
}
}
public class Main {
public static void main(String[] args) {
Message message = new Message();
Thread producerThread = new Thread(new Producer(message));
Thread consumerThread = new Thread(new Consumer(message));
producerThread.start();
consumerThread.start();
}
}
この例には、メッセージ オブジェクトを表す Message クラスがあります。Message クラスの putMessage メソッドは、メッセージを生成し、そのメッセージを content 変数に格納するために使用されます。getMessage メソッドは、メッセージを消費し、保存されているメッセージの内容を返すために使用されます。どちらのメソッドも synchronized キーワードを使用して同期を実現し、スレッドの安全性を確保します。
1 つのスレッドでメッセージを生成するための Runnable インターフェイスを実装するプロデューサー クラスがあります。Consumer クラスは、別のスレッドでメッセージを消費するための Runnable インターフェイスも実装します。
Main クラスの main メソッドでは、Message オブジェクトが作成され、プロデューサー スレッドとコンシューマー スレッドが作成されます。start メソッドを呼び出して 2 つのスレッドを開始すると、メッセージの生成と消費が同時に行われます。
コンソール出力では、プロデューサー スレッドとコンシューマー スレッドが交互にメッセージを出力し、共有の Message オブジェクトを通じて通信していることがわかります。
2. 待機/通知メカニズム
Java には、スレッド間の待機と通知のための wait、notify、および NoticeAll メソッドが用意されています。スレッドは、wait メソッドを呼び出して、別のスレッドが同じオブジェクトの Notice メソッドまたは NotifyAll メソッドを呼び出してスレッドをウェイクアップするまで、自身の実行を一時停止できます。
以下は、待機/通知メカニズムを使用したスレッド通信のサンプル コードです。
class Message {
private String content;
private boolean hasNewMessage = false;
public synchronized void putMessage(String content) {
while (hasNewMessage) {
try {
wait(); // 等待直到消息被消费
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
this.content = content;
hasNewMessage = true;
notifyAll(); // 唤醒等待的线程
}
public synchronized String getMessage() {
while (!hasNewMessage) {
try {
wait(); // 等待直到有新消息
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
hasNewMessage = false;
notifyAll(); // 唤醒等待的线程
return content;
}
}
class Producer implements Runnable {
private Message message;
public Producer(Message message) {
this.message = message;
}
public void run() {
String[] messages = {
"Hello", "World", "Goodbye" };
for (String msg : messages) {
message.putMessage(msg);
System.out.println("Producer: " + msg);
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
message.putMessage("Done");
}
}
class Consumer implements Runnable {
private Message message;
public Consumer(Message message) {
this.message = message;
}
public void run() {
String msg = "";
while (!msg.equals("Done")) {
msg = message.getMessage();
System.out.println("Consumer: " + msg);
}
}
}
public class Main {
public static void main(String[] args) {
Message message = new Message();
Thread producerThread = new Thread(new Producer(message));
Thread consumerThread = new Thread(new Consumer(message));
producerThread.start();
consumerThread.start();
}
}
この例のコードは前の例と同じです。違いは、putMessage メソッドと getMessage メソッドは wait メソッドとnotifyAll メソッドを使用してスレッド間の待機と通知を行うことです。putMessage メソッドが wait を呼び出すと、オブジェクトのロックが解放され、ウェイクアップされるのを待ちます。getMessage メソッドが NoticeAll を呼び出すと、待機中のスレッドが起動され、オブジェクトのロックが再取得されます。
このようにして、プロデューサー スレッドは、新しいメッセージがない場合、コンシューマー スレッドがメッセージを消費し、notifyAll メソッドを呼び出すまで待機します。同様に、コンシューマ スレッドは、新しいメッセージがない場合、プロデューサ スレッドが新しいメッセージを生成し、notifyAll メソッドを呼び出すまで待機します。
共有変数であれ待機/通知メカニズムであれ、Java はスレッド間通信を実現するためのさまざまなメソッドを提供します。適切な方法の選択は、特定のアプリケーションのシナリオと要件によって異なります。