等待唤醒机制

简介: 等待唤醒机制

等待唤醒机制(Wait-Notify)是一种线程间协作的机制,用于实现线程之间的通信和同步。它主要涉及两个操作:等待(wait)和唤醒(notify)。

等待唤醒机制通常应用于多线程环境下,其中一个线程等待某个条件得到满足,而另一个线程负责在满足条件时通知等待的线程继续执行。

具体来说,当一个线程调用对象的wait()方法时,它会释放当前持有的锁,并且进入等待状态,直到其他线程通过notify()或notifyAll()方法来唤醒它。被唤醒的线程将重新尝试获取锁,然后继续执行。

而唤醒线程通常在满足某个条件时,通过调用对象的notify()或notifyAll()方法来通知正在等待的线程继续执行。notify()方法会随机选择一个等待线程进行唤醒,而notifyAll()方法会唤醒所有等待的线程,使它们进入就绪状态,等待获取锁以继续执行。

等待唤醒机制提供了一种有效的线程间协作方式,使得线程可以根据特定条件进行等待和唤醒,从而实现对共享资源的更好利用和管理。

需要注意的是,等待唤醒机制必须在同步代码块中使用,即对共享对象进行操作,以确保正确的并发控制和线程间的协作。在调用wait()、notify()或notifyAll()方法之前,线程必须先获得相应对象的锁,否则会抛出IllegalMonitorStateException异常。

下面是一个简单的Java代码示例,演示了等待唤醒机制的实现:

public class WaitNotifyExample {
    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();
    }
}
class Message {
    private String content;
    private boolean isProduced;
    public synchronized void produce(String content) {
        while (isProduced) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        this.content = content;
        System.out.println("Produced: " + content);
        isProduced = true;
        notify();
    }
    public synchronized String consume() {
        while (!isProduced) {
            try {
                wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        String consumedContent = content;
        System.out.println("Consumed: " + consumedContent);
        isProduced = false;
        notify();
        return consumedContent;
    }
}
class Producer implements Runnable {
    private Message message;
    public Producer(Message message) {
        this.message = message;
    }
    @Override
    public void run() {
        String[] contents = {"Message 1", "Message 2", "Message 3"};
        for (String content : contents) {
            message.produce(content);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
class Consumer implements Runnable {
    private Message message;
    public Consumer(Message message) {
        this.message = message;
    }
    @Override
    public void run() {
        for (int i = 0; i < 3; i++) {
            String consumedContent = message.consume();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

下面我们逐步解释代码的细节:

  1. 首先,我们创建了一个Message类,用于消息传递。该类包含了一个消息内容content和一个表示是否已经生产消息的标志isProduced
  2. Message类中的produce()方法用于生产消息。在该方法中,使用synchronized关键字确保多个线程在访问produce()方法时的互斥性。
  3. produce()方法中,使用while循环来检查是否已经生产消息。如果isProducedtrue,说明已经生产了消息,则调用wait()方法让当前线程进入等待状态,直到被其他线程调用notify()方法唤醒。
  4. 当前线程被唤醒后,会继续执行后续代码,将传入的消息内容赋值给content变量,并将isProduced标志设置为true,表示消息已经生产。然后通过notify()方法唤醒在等待的其他线程。
  5. Message类中的consume()方法用于消费消息。同样,使用synchronized关键字确保多个线程在访问consume()方法时的互斥性。
  6. consume()方法中,同样使用while循环来检查是否已经生产消息。如果isProducedfalse,说明还没有生产消息,则调用wait()方法让当前线程进入等待状态,直到被其他线程调用notify()方法唤醒。
  7. 当前线程被唤醒后,会继续执行后续代码,将content变量的值作为消费的消息内容,并将isProduced标志设置为false,表示消息已经被消费。然后通过notify()方法唤醒在等待的其他线程。
  8. 在主程序中,我们创建了一个Message对象和两个线程:生产者线程Producer和消费者线程Consumer
  9. 生产者线程通过调用message.produce(content)方法来生产消息。在示例中,我们定义了3个消息内容,分别进行生产,并且每次生产完后等待1秒钟。
  10. 消费者线程通过调用message.consume()方法来消费消息。在示例中,我们简单地循环3次消费消息,并且每次消费后等待1秒钟。

通过以上操作,生产者和消费者线程之间进行消息的生产和消费,并且使用了等待唤醒机制进行线程间的通信和同步。当消息已经生产时,生产者线程进入等待状态,等待消费者线程消费完毕后唤醒;当消息还没有被生产时,消费者线程进入等待状态,等待生产者线程生产完毕后唤醒。

需要注意的是,为了保证正确的并发控制和线程间的协作,必须在调用wait()notify()notifyAll()方法之前先获取锁(即使用synchronized关键字修饰的方法或代码块),否则会抛出IllegalMonitorStateException异常。


相关文章
|
机器学习/深度学习 人工智能 算法
AIGC革新商业模式与用户体验
【1月更文挑战第19天】AIGC革新商业模式与用户体验
476 1
AIGC革新商业模式与用户体验
|
存储 弹性计算 运维
阿里云服务器快照备份数据怎么下载到本地保存?
阿里云服务器数据备份并下载保存到本地方法流程,可以使用阿里云快照备份云服务器上的数据,由于快照不能直接下载,然后使用快照创建自定义镜像,然后再将镜像导出下载到本地
8305 0
阿里云服务器快照备份数据怎么下载到本地保存?
|
9月前
|
人工智能 算法 物联网
AI大模型爆火背后,C++ 如何助力 AI 开发大显身手?
AI大模型爆火背后,C++ 如何助力 AI 开发大显身手?
|
Unix Linux Python
`subprocess`模块允许你启动新的应用程序,连接到它们的输入/输出/错误管道,并获取它们的返回码。
`subprocess`模块允许你启动新的应用程序,连接到它们的输入/输出/错误管道,并获取它们的返回码。
|
Web App开发 Linux 开发工具
告别卡顿,畅享GitHub:国内开发者必看的五大加速访问与下载技巧
【8月更文挑战第4天】告别卡顿,畅享GitHub:国内开发者必看的五大加速访问与下载技巧
告别卡顿,畅享GitHub:国内开发者必看的五大加速访问与下载技巧
|
存储 安全 开发工具
GitHub 支持双因素认证(2FA)
【9月更文挑战第29天】
1677 6
|
计算机视觉 开发者 Python
【PyCharm中PIL/Pillow的安装】
【PyCharm中PIL/Pillow的安装】
525 0
|
数据采集 人工智能 数据可视化
Python selenium爬虫被检测到,该怎么破?
Python selenium爬虫被检测到,该怎么破?
1708 9
|
存储 SQL 关系型数据库
(四)MySQL之索引初识篇:索引机制、索引分类、索引使用与管理综述
本篇中就对MySQL的索引机制有了全面认知,从索引的由来,到索引概述、索引管理、索引分类、唯一/全文/联合/空间索引的创建与使用等内容,进行了全面概述。
605 0
|
数据可视化 算法 Java
了解go语言运行时工具的作用
【5月更文挑战第16天】本文简介`runtime`库提供系统调用包装、执行跟踪、内存分配统计、运行时指标和剖析支持。`internal/syscall`封装系统调用,保证uintptr参数有效。`trace`用于执行跟踪,捕获各种事件,如goroutine活动、系统调用和GC事件。`ReadMemStats`提供内存分配器统计。`metrics`接口访问运行时定义的度量,包括CPU使用、GC和内存信息。`coverage`支持代码覆盖率分析,`cgo`处理C语言交互,`pprof`提供性能剖析工具集成。这些功能帮助优化和理解Go程序的运行行为。
199 6