线程通信:notify()和notifyAll()

一、线程通信是什么

线程通信是指在多线程编程中,不同线程之间相互交换信息、共享数据或进行协调操作的机制。线程通信的目的是实现线程之间的协作和协调,以完成复杂的任务或解决特定的问题。

上面的描述太过官方,我们来用一个案例说明一下线程通信到底是个什么东西。

sequenceDiagram 顾客线程->>服务员线程: 发起点餐请求 服务员线程->>厨师线程: 传递订单信息 厨师线程-->>厨师线程: 准备食物 厨师线程-->>服务员线程: 完成消息 服务员线程->>顾客线程: 通知可以上菜 顾客线程-->>顾客线程: 开始用餐
  • 假设有以下场景描述:

    • 顾客线程:代表顾客,负责去餐厅、下单、等待并消费食物。

    • 服务员线程:代表服务员,负责接收顾客的订单,并将食物上菜。

    • 厨师线程:代表厨师,负责根据订单准备食物。

  • 线程通信的过程如下:

    • 顾客线程进入餐厅,发起点餐请求。顾客线程向服务员线程发送点餐请求的消息。

    • 服务员线程接收到顾客的点餐请求,将订单信息传递给厨师线程。服务员线程向厨师线程发送订单信息的消息。

    • 厨师线程接收到订单信息后,开始准备食物。在准备食物的过程中,厨师线程可以通过某种方式通知服务员线程正在做饭,以防止服务员线程过早地将订单上菜。

    • 厨师线程完成食物的准备后,向服务员线程发送完成消息,表示可以上菜了。

    • 服务员线程接收到厨师的完成消息后,将食物上菜,并通知顾客线程可以开始用餐。

    • 顾客线程接收到用餐通知后,开始享用食物。

在这个场景中,顾客线程、服务员线程和厨师线程通过消息的发送和接收进行通信。顾客线程向服务员线程发送点餐请求的消息,服务员线程向厨师线程发送订单信息的消息,厨师线程向服务员线程发送完成消息,服务员线程向顾客线程发送用餐通知的消息。通过这样的线程通信,实现了顾客点餐、厨师做饭、服务员上菜和顾客用餐的协同工作。

二、专一的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类中用于线程间通信的方法。它们的区别如下所示:

graph LR Q(区别) Q --> A(notify方法) --> B{等待的线程} B --> C(唤醒一个线程) C --> D(竞争对象锁) D --> E(继续执行) Q --> F(notifyAll方法) --> G{等待的线程} G --> H(唤醒所有线程) H --> I(竞争对象锁) I --> J(继续执行) style B fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style C fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style D fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style E fill:#98FB98,stroke:#98FB98,stroke-width:2px style G fill:#FFC0CB,stroke:#FFC0CB,stroke-width:2px style H fill:#FFA07A,stroke:#FFA07A,stroke-width:2px style I fill:#FFFFE0,stroke:#FFFFE0,stroke-width:2px style J fill:#98FB98,stroke:#98FB98,stroke-width:2px style A fill:#EEDD82,stroke:#EEDD82,stroke-width:2px style F fill:#EEDD82,stroke:#EEDD82,stroke-width:2px

1、notify() : notify()方法用于唤醒在对象上等待的单个线程。如果有多个线程在对象上等待,那么只有一个线程会被唤醒,而其他线程仍然处于等待状态。具体唤醒哪个线程是不确定的,取决于操作系统的调度。

2、notifyAll() : notifyAll()方法用于唤醒在对象上等待的所有线程。它会唤醒所有等待的线程,并让它们去竞争对象锁。只有获取到对象锁的线程才能继续执行,其他线程将继续处于等待状态。

注意:notify()notifyAll()方法必须在持有对象的锁的情况下调用,否则会抛出IllegalMonitorStateException异常。

五、总结

notify()notifyAll()都是Java中用于线程间通信的方法。它们的作用是唤醒在对象上等待的线程。

notify()方法像是一个专一的纯爱战士,用于唤醒在对象上等待的单个线程。如果有多个线程在等待,只有其中一个线程会被唤醒,但具体唤醒哪个线程是不确定的。被唤醒的线程将竞争对象锁并继续执行。

notifyAll()方法像是一个海王,用于唤醒在对象上等待的所有线程。所有等待的线程都会被唤醒,并尝试竞争对象锁。只有获取到锁的线程才能继续执行,其他线程仍然处于等待状态。

这个世界上少不了纯爱战士和海王,选择使用notify()还是notifyAll()方法取决于具体的需求和逻辑。如果希望唤醒所有等待的线程,可以使用notifyAll()方法。如果只需要唤醒其中一个线程,可以使用notify()方法。

希望本文对您有所帮助。如果有任何错误或建议,请随时指正和提出。

同时,如果您觉得这篇文章有价值,请考虑点赞和收藏。这将激励我进一步改进和创作更多有用的内容。

感谢您的支持和理解!

相关推荐
救救孩子把13 分钟前
深入理解 Java 对象的内存布局
java
落落落sss16 分钟前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
万物皆字节21 分钟前
maven指定模块快速打包idea插件Quick Maven Package
java
夜雨翦春韭28 分钟前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
我行我素,向往自由35 分钟前
速成java记录(上)
java·速成
一直学习永不止步40 分钟前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
邵泽明41 分钟前
面试知识储备-多线程
java·面试·职场和发展
Yvemil71 小时前
MQ 架构设计原理与消息中间件详解(二)
开发语言·后端·ruby
程序员是干活的1 小时前
私家车开车回家过节会发生什么事情
java·开发语言·软件构建·1024程序员节
煸橙干儿~~1 小时前
分析JS Crash(进程崩溃)
java·前端·javascript