等待唤醒机制

简介: 等待唤醒机制

等待唤醒机制(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循环来检查是否已经生产消息。如果isProduced为true,说明已经生产了消息,则调用wait()方法让当前线程进入等待状态,直到被其他线程调用notify()方法唤醒。
  4. 当前线程被唤醒后,会继续执行后续代码,将传入的消息内容赋值给content变量,并将isProduced标志设置为true,表示消息已经生产。然后通过notify()方法唤醒在等待的其他线程。
  5. Message类中的consume()方法用于消费消息。同样,使用synchronized关键字确保多个线程在访问consume()方法时的互斥性。
  6. 在consume()方法中,同样使用while循环来检查是否已经生产消息。如果isProduced为false,说明还没有生产消息,则调用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异常。


相关文章
|
Java 微服务 Spring
@EnableDiscoveryClient注解的作用
@EnableDiscoveryClient注解的作用 @EnableDiscoveryClient 及@EnableEurekaClient 类似,都是将一个微服务注册到Eureka Server(或其他 服务发现组件,例如Zookeeper、Consul等)
2401 0
|
XML Java 数据库
探索 Spring Boot 中的 @Configuration 注解:核心概念与应用
【4月更文挑战第20天】在 Spring Boot 项目中,@Configuration 注解扮演了一个关键角色,它标识一个类作为配置源,这些配置用于定义和管理 Spring 应用程序中的 Bean
3120 7
|
缓存 安全 Linux
使用macof发起MAC地址泛洪攻击
使用macof发起MAC地址泛洪攻击
590 1
|
数据安全/隐私保护
5分钟注册一个可用Gemini的谷歌邮箱账号
5分钟注册一个可用Gemini的谷歌邮箱账号
8476 24
5分钟注册一个可用Gemini的谷歌邮箱账号
|
存储 SQL 关系型数据库
(四)MySQL之索引初识篇:索引机制、索引分类、索引使用与管理综述
本篇中就对MySQL的索引机制有了全面认知,从索引的由来,到索引概述、索引管理、索引分类、唯一/全文/联合/空间索引的创建与使用等内容,进行了全面概述。
1017 0
|
Java Maven Spring
【Spring Boot自动装配原理详解与常见面试题】—— 每天一点小知识(下)
【Spring Boot自动装配原理详解与常见面试题】—— 每天一点小知识(下)
1069 0
|
Apache 数据安全/隐私保护
HAProxy的高级配置选项-ACL篇之基于策略的访问控制
这篇文章介绍了HAProxy的高级配置选项,特别是如何使用ACL(访问控制列表)进行基于策略的访问控制,通过实战案例展示了如何配置HAProxy以允许或拒绝来自特定源地址的访问。
501 7
HAProxy的高级配置选项-ACL篇之基于策略的访问控制
|
应用服务中间件 PHP Apache
HAProxy的高级配置选项-ACL篇之匹配访问路径案例
这篇文章介绍了HAProxy的高级配置选项,特别是如何使用ACL(访问控制列表)匹配访问路径以实现不同请求路径的流量分发到不同后端服务器的案例,通过实战配置展示了如何基于URL路径将请求定向到处理静态或动态内容的服务器。
497 5
HAProxy的高级配置选项-ACL篇之匹配访问路径案例
|
存储 安全 开发工具
GitHub 支持双因素认证(2FA)
【9月更文挑战第29天】
3872 6