什么是等待唤醒机制?
本篇将会带大家了解并发线程中的一个经典模式,并且详细讲解他的执行流程,尝试使用代码来实现
唤醒等待机制(Wait And Notify)又叫生产者消费者模式,是一种多线程编程中常见的同步控制技术,主要用于线程之间的协调和通信。它通常由等待(wait) 和 唤醒(notify) 两个操作构成。通过这种机制,一个线程可以挂起自身并等待条件满足,而另一个线程可以在满足条件时唤醒等待的线程
当两个线程并发执行时,常规情况之下,他们抢夺CPU的情况是完全随机的:
但是等待唤醒机制会打破这种随机的规则,他会让这两个线程交替执行
生产者
生产者的任务是生成数据,并将其放入缓冲区供消费者使用
- 判断缓冲区中是否有数据
- 有,则等待
- 没有,则生产数据并放入缓冲区
- 唤醒消费者线程
消费者
消费者的任务是从缓冲区中取出数据进行处理
- 判断缓冲区中是否有数据
- 没有,则等待
- 有,则消费数据
- 唤醒生产者进程
当生产者发现缓冲区已满,或者消费者发现缓冲区为空时,它们会调用阻塞机制进入等待状态,释放占用的资源(如锁)
当缓冲区状态发生变化时(例如生产者生成了新数据,或者消费者消耗了数据),会通知等待的一方继续执行
代码示例
接下来将用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次数据
可以看到两个线程的执行情况就是我们最开始说的交替执行
1635

被折叠的 条评论
为什么被折叠?



