什么是等待唤醒机制?

什么是等待唤醒机制?


本篇将会带大家了解并发线程中的一个经典模式,并且详细讲解他的执行流程,尝试使用代码来实现

唤醒等待机制(Wait And Notify)又叫生产者消费者模式,是一种多线程编程中常见的同步控制技术,主要用于线程之间的协调和通信。它通常由等待(wait)唤醒(notify) 两个操作构成。通过这种机制,一个线程可以挂起自身并等待条件满足,而另一个线程可以在满足条件时唤醒等待的线程

当两个线程并发执行时,常规情况之下,他们抢夺CPU的情况是完全随机的:

线程A
线程B
线程A
线程A
线程A
线程B
线程B
线程A
线程A
线程A

但是等待唤醒机制会打破这种随机的规则,他会让这两个线程交替执行

线程A
线程B
线程A
线程B
线程A
线程B
线程A
线程B
线程A
线程B

生产者

生产者的任务是生成数据,并将其放入缓冲区供消费者使用

  1. 判断缓冲区中是否有数据
  2. 有,则等待
  3. 没有,则生产数据并放入缓冲区
  4. 唤醒消费者线程

消费者

消费者的任务是从缓冲区中取出数据进行处理

  1. 判断缓冲区中是否有数据
  2. 没有,则等待
  3. 有,则消费数据
  4. 唤醒生产者进程
消费者模块
生产者模块
生产数据
消费数据
缓冲区有空间
缓冲区有数据
消费者
消费者等待
缓冲区空?
继续消费
缓冲区
生产者
生产者等待
缓冲区满?
继续生产

当生产者发现缓冲区已满,或者消费者发现缓冲区为空时,它们会调用阻塞机制进入等待状态,释放占用的资源(如锁)
当缓冲区状态发生变化时(例如生产者生成了新数据,或者消费者消耗了数据),会通知等待的一方继续执行


代码示例

接下来将用Java代码来实现一个简单的唤醒等待机制

缓冲区

缓冲区的主要作用就是控制和存储,即可以做缓冲数据的容器,也可以存放锁对象和控制消费者和生产者的执行终止条件

package juc;

public class Buffer {
    // 标识缓冲区是否有数据 0:有 1:没有
    public static int Flag = 0;
    // 数据传输上限
    public static int num = 10;
    // 锁对象
    public static Object lock = new Object();
}

生产者

会先进行数据上限判断,如果数据未上限,并且缓冲区内没有数据,则会生产数据,再唤醒消费线程消费数据,如果缓冲区内已有数据,则会等待,直到数据被消费线程消费



public class Producer extends Thread{
    @Override
    public void run() {
        while (true){
            synchronized (Buffer.lock){ // 加锁
                // 判断是否传输上限
                if (Buffer.num == 0){
                    break;
                }
                else {
                    // 判断缓冲区是否有数据
                    if (Buffer.Flag != 0){ // 有数据
                        try {
                            Buffer.lock.wait(); // 等待
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    // 缓冲区没有数据
                    else {
                        System.out.println("正在生产数据");
                        // 修改缓冲区状态
                        Buffer.Flag =1;
                        // 唤醒消费线程
                        Buffer.lock.notifyAll();
                    }
                }
            }
        }


    }
}

消费者

同样会先进行数据上限判断,如果数据未上限,并且缓冲区内有数据,则会消费数据,再唤醒生产线程生产新的数据,如果缓冲区内没有有数据,则会等待,等待生产者线程生产新的数据后被唤醒



public class Consumer extends Thread{
    @Override
    public void run() {
        while (true){
            synchronized (Buffer.lock){ // 加锁
                // 判断是否传输上限
                if (Buffer.num == 0){
                    break;
                }
                else {
                    // 判断缓冲区是否有数据
                    if (Buffer.Flag == 0){  // 没有数据
                        try {
                            Buffer.lock.wait(); // 等待
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    else { // 缓冲区有数据
                        // 数据上限减一
                        Buffer.num --;
                        System.out.println("还能消费"+ Buffer.num+"次数据");
                        // 唤醒生产者线程
                        Buffer.lock.notifyAll();
                        // 修改缓冲区状态
                        Buffer.Flag = 0;
                    }
                }
            }
        }
    }
}

最后我们需要一个测试类来测试我们的等待唤醒机制模块的真实运行情况:



public class WaitTest {
    public static void main(String[] args) {
        // 创建线程对象
        Producer producer = new Producer();
        Consumer consumer = new Consumer();
        
        producer.setName("生产者");
        consumer.setName("消费者");
        
        // 启动线程
        producer.start();
        consumer.start();

    }
}

运行之后我们就会得到如下运行结果

正在生产数据
还能消费9次数据
正在生产数据
还能消费8次数据
正在生产数据
还能消费7次数据
正在生产数据
还能消费6次数据
正在生产数据
还能消费5次数据
正在生产数据
还能消费4次数据
正在生产数据
还能消费3次数据
正在生产数据
还能消费2次数据
正在生产数据
还能消费1次数据
正在生产数据
还能消费0次数据

可以看到两个线程的执行情况就是我们最开始说的交替执行

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值