一、线程通信是什么
线程通信是指在多线程编程中,不同线程之间相互交换信息、共享数据或进行协调操作的机制。线程通信的目的是实现线程之间的协作和协调,以完成复杂的任务或解决特定的问题。
上面的描述太过官方,我们来用一个案例说明一下线程通信到底是个什么东西。
假设有以下场景描述:
顾客线程:代表顾客,负责去餐厅、下单、等待并消费食物。
服务员线程:代表服务员,负责接收顾客的订单,并将食物上菜。
厨师线程:代表厨师,负责根据订单准备食物。
线程通信的过程如下:
顾客线程进入餐厅,发起点餐请求。顾客线程向服务员线程发送点餐请求的消息。
服务员线程接收到顾客的点餐请求,将订单信息传递给厨师线程。服务员线程向厨师线程发送订单信息的消息。
厨师线程接收到订单信息后,开始准备食物。在准备食物的过程中,厨师线程可以通过某种方式通知服务员线程正在做饭,以防止服务员线程过早地将订单上菜。
厨师线程完成食物的准备后,向服务员线程发送完成消息,表示可以上菜了。
服务员线程接收到厨师的完成消息后,将食物上菜,并通知顾客线程可以开始用餐。
顾客线程接收到用餐通知后,开始享用食物。
在这个场景中,顾客线程、服务员线程和厨师线程通过消息的发送和接收进行通信。顾客线程向服务员线程发送点餐请求的消息,服务员线程向厨师线程发送订单信息的消息,厨师线程向服务员线程发送完成消息,服务员线程向顾客线程发送用餐通知的消息。通过这样的线程通信,实现了顾客点餐、厨师做饭、服务员上菜和顾客用餐的协同工作。
二、专一的notify()
notify()
方法是Java
中用于线程间通信的方法之一,它的作用是唤醒在对象上等待的单个线程。
当一个线程调用某个对象的wait()
方法后,它会释放该对象的锁,并进入等待状态,直到其他线程调用该对象的notify()
方法来唤醒它。
notify()
方法的作用是选择唤醒在对象上等待的一个线程。具体唤醒哪个线程是不确定的,取决于操作系统的调度。被唤醒的线程将重新竞争对象的锁,一旦获取到锁,它就从wait()
方法返回,并继续执行。
notify()
方法必须在持有对象的锁的情况下调用,否则会抛出IllegalMonitorStateException
异常。
使用notify()
方法可以实现一些线程间的协作机制。例如,当一个线程完成了某个任务,它可以调用notify()
方法来唤醒等待该任务结果的线程,以便它们继续执行后续的操作。这样可以在多线程编程中实现线程间的同步和协调。
notify()
是一个专一的纯爱战士,只会唤醒一个线程,如果有多个线程在对象上等待,只有一个线程会被唤醒,其他线程仍然处于等待状态。如果需要唤醒所有等待的线程,可以使用notifyAll()
方法。
2.1 一个案例演示一下notify()
使用Message类的示例代码,展示了如何创建生产者和消费者线程并使用
produce()
和consume()
方法进行消息传递
java
public class Message {
private String content;
private boolean isMessageAvailable = false;
public synchronized void produce(String message) {
while (isMessageAvailable) {
try {
wait(); // 等待直到消息被消费
} catch (InterruptedException e) {
e.printStackTrace();
}
}
content = message;
isMessageAvailable = true;
System.out.println("Producer produced: " + content);
notify(); // 唤醒等待的消费者线程
}
public synchronized void consume() {
while (!isMessageAvailable) {
try {
wait(); // 等待直到有消息可消费
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Consumer consumed: " + content);
isMessageAvailable = false;
notify(); // 唤醒等待的生产者线程
}
}
public class Main {
public static void main(String[] args) {
Message message = new Message();
// 创建生产者线程
Thread producerThread = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
String content = "Message " + i;
message.produce(content);
try {
Thread.sleep(1000); // 生产者线程休眠1秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 创建消费者线程
Thread consumerThread = new Thread(() -> {
for (int i = 1; i <= 5; i++) {
message.consume();
try {
Thread.sleep(2000); // 消费者线程休眠2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// 启动生产者和消费者线程
producerThread.start();
consumerThread.start();
// 等待线程执行完成
try {
producerThread.join();
consumerThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
这个示例中,我们创建了一个
Message
对象,并使用producerThread
线程作为生产者,负责生产消息,以及consumerThread
线程作为消费者,负责消费消息。生产者线程通过调用
message.produce(content)
方法来生产消息,每次生产完消息后休眠1秒。消费者线程通过调用
message.consume()
方法来消费消息,每次消费完消息后休眠2秒。最后,我们启动生产者和消费者线程,并使用
join()
方法等待它们执行完成。
三、喜欢养鱼的notifyAll()
notifyAll()
方法是Java
中用于线程间通信的方法之一,它的作用是唤醒在对象上等待的所有线程。
当一个线程调用某个对象的wait()
方法后,它会释放该对象的锁,并进入等待状态,直到其他线程调用该对象的notify()
或notifyAll()
方法来唤醒它。
notifyAll()
方法的作用是唤醒在对象上等待的所有线程,并让它们去竞争对象锁。一旦一个线程获取到锁,它就从wait()
方法返回,并继续执行。其他未获取到锁的线程仍然处于等待状态。
notifyAll()
方法必须在持有对象的锁的情况下调用,否则会抛出IllegalMonitorStateException
异常。
使用notifyAll()
方法可以实现一些线程间的协作机制。例如,当某个条件满足时,调用notifyAll()
方法可以唤醒所有等待该条件的线程,让它们继续执行后续的操作。这样可以在多线程编程中实现线程间的同步和协调。
notifyAll()
方法会唤醒所有等待的线程,包括生产者线程和消费者线程。因此,在设计线程间通信时,需要仔细考虑使用notify()
还是notifyAll()
方法,以满足具体的需求和逻辑。
总结起来,notifyAll()
就是一个海王,他的的作用是唤醒在对象上等待的所有线程,让它们竞争对象锁并继续执行。这种方式可以用于实现线程间的同步和协调。
3.1 一个案例演示一下notifyAll()
有一个
NotificationManager
类,用于管理通知对象。该类有两个方法:
addNotification()
用于添加新的通知
notifyNotifications()
用于通知所有等待的线程
java
import java.util.ArrayList;
import java.util.List;
public class NotificationManager {
private List<Notification> notifications = new ArrayList<>();
public synchronized void addNotification(Notification notification) {
notifications.add(notification);
System.out.println("New notification added: " + notification.getMessage());
notifyAll(); // 唤醒所有等待的线程
}
public synchronized void notifyNotifications() {
while (notifications.isEmpty()) {
try {
wait(); // 等待直到有新的通知添加
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Notifying all notifications:");
for (Notification notification : notifications) {
System.out.println(notification.getMessage());
}
notifications.clear();
}
}
public class Main {
public static void main(String[] args) {
NotificationManager manager = new NotificationManager();
Thread producerThread1 = new Thread(() -> {
manager.addNotification(new Notification("Notification 1"));
});
Thread producerThread2 = new Thread(() -> {
manager.addNotification(new Notification("Notification 2"));
});
Thread consumerThread = new Thread(() -> {
manager.notifyNotifications();
});
producerThread1.start();
producerThread2.start();
try {
Thread.sleep(1000); // 等待一段时间,确保通知被添加
} catch (InterruptedException e) {
e.printStackTrace();
}
consumerThread.start();
}
}
在这个示例中,我们创建了一个
NotificationManager
对象,并创建了两个生产者线程producerThread1``和producerThread2
,它们负责添加通知到NotificationManager
中。然后,我们启动了一个消费者线程
consumerThread
,它负责通知所有等待的线程。运行这段代码,你将看到两个生产者线程分别添加了两个通知,然后消费者线程通知并打印出这些通知的消息。
通过使用
notifyAll()
方法,我们可以确保所有等待的线程在有新通知时被唤醒,并处理这些通知。这样可以实现线程间的同步和协调,确保通知的及时处理。
四、notify()和 notifyAll()有什么区别
在Java
的多线程编程中,notify()
和notifyAll()
是Object
类中用于线程间通信的方法。它们的区别如下所示:
1、notify() : notify()
方法用于唤醒在对象上等待的单个线程。如果有多个线程在对象上等待,那么只有一个线程会被唤醒,而其他线程仍然处于等待状态。具体唤醒哪个线程是不确定的,取决于操作系统的调度。
2、notifyAll() : notifyAll()
方法用于唤醒在对象上等待的所有线程。它会唤醒所有等待的线程,并让它们去竞争对象锁。只有获取到对象锁的线程才能继续执行,其他线程将继续处于等待状态。
注意:
notify()
和notifyAll()
方法必须在持有对象的锁的情况下调用,否则会抛出IllegalMonitorStateException
异常。
五、总结
notify()
和notifyAll()
都是Java
中用于线程间通信的方法。它们的作用是唤醒在对象上等待的线程。
notify()
方法像是一个专一的纯爱战士,用于唤醒在对象上等待的单个线程。如果有多个线程在等待,只有其中一个线程会被唤醒,但具体唤醒哪个线程是不确定的。被唤醒的线程将竞争对象锁并继续执行。
notifyAll()
方法像是一个海王,用于唤醒在对象上等待的所有线程。所有等待的线程都会被唤醒,并尝试竞争对象锁。只有获取到锁的线程才能继续执行,其他线程仍然处于等待状态。
这个世界上少不了纯爱战士和海王,选择使用notify()
还是notifyAll()
方法取决于具体的需求和逻辑。如果希望唤醒所有等待的线程,可以使用notifyAll()
方法。如果只需要唤醒其中一个线程,可以使用notify()
方法。
希望本文对您有所帮助。如果有任何错误或建议,请随时指正和提出。
同时,如果您觉得这篇文章有价值,请考虑点赞和收藏。这将激励我进一步改进和创作更多有用的内容。
感谢您的支持和理解!