当前位置: 首页 > news >正文

阻塞队列:原理、应用及实现

阻塞队列:原理、应用及实现

    • 什么是阻塞队列
    • 以生产消费者模型形象地理解阻塞队列
    • 阻塞队列实现生产消费者模型
    • 模拟实现阻塞队列实现生产消费者模型

什么是阻塞队列

阻塞队列是一种特殊且实用的队列数据结构,它同样遵循 “先进先出” 的原则。与普通队列不同的是,阻塞队列是线程安全的,这使得它在多线程编程中扮演着重要角色。

阻塞队列具有以下两个关键特性:

  • 当队列已满时,如果有线程尝试将元素入队列,该线程将会被阻塞,直到有其他线程从队列中取走元素,使得队列腾出空间。
  • 当队列为空时,若有线程试图从队列中出队列,此线程也会被阻塞,直至有其他线程向队列中插入新的元素。

阻塞队列的一个经典应用场景便是 “生产者消费者模型”。这种模型在软件开发中被广泛使用,能够有效地协调不同线程之间的工作,提高程序的性能和稳定性。

以生产消费者模型形象地理解阻塞队列

为了更直观地理解阻塞队列的工作原理,我们以一个具体的生产消费者场景为例。假设有四个生产者线程和一个消费者线程:

  • 每个生产者线程每单位时间能够生产 4 个面包。
  • 消费者线程每单位时间只能取走 1 个面包。
    在这里插入图片描述

在这种情况下,由于生产者的生产速度远快于消费者的消费速度,每单位时间会有 3 个面包进入阻塞队列,等待消费者线程来取。并且在这个过程中,生产者线程不会停止生产。
在这里插入图片描述

随着时间的推移,当阻塞队列被填满,没有空位时,生产者线程就会因为无法继续入队列而停止生产,直到消费者线程从队列中取走一些面包,腾出空间。
在这里插入图片描述

阻塞队列实现生产消费者模型

在 Java 中,BlockingQueue接口及其实现类为我们提供了便捷的方式来实现生产消费者模型。以下是 BlockingQueue 的构造方法示例图:

在这里插入图片描述

下面通过一段 Java 代码来具体实现生产消费者模型(为了便于观察和理解,我们创建了一个固定容量的阻塞队列):

import java.util.Random;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

public class Main {
    public static void main(String[] args) {
        Random random = new Random();
        // 首先创建一个阻塞队列,容量为 10
        BlockingDeque<Integer> blockingDeque = new LinkedBlockingDeque<>(10);

        // 创建生产者线程
        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    int value = random.nextInt(100);
                    System.out.println("生产元素:" + value);
                    blockingDeque.put(value);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        // 创建消费者线程
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    System.out.println("消费元素:" + blockingDeque.take());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t1.start();
        t2.start();
    }
}

通过上述代码,生产者线程不断生产随机整数并放入阻塞队列,而消费者线程则从队列中取出元素并打印,同时每隔 1 秒消费一次,模拟实际的消费速度。
在这里插入图片描述

模拟实现阻塞队列实现生产消费者模型

除了使用 Java 提供的现成阻塞队列实现类,我们还可以自己模拟实现一个固定容量的阻塞队列。在这个实现过程中,需要重点关注以下三个方面:

  1. 确保线程安全,避免多个线程同时访问队列时出现数据不一致的问题。
  2. 当队列为空时,消费者线程需要阻塞等待,直到队列中有新的元素可供消费。
  3. 当队列为满时,生产者线程需要阻塞等待,直到队列中有元素被消费,腾出空间。

以下是模拟实现的代码:

public class MyBlockqueue {
    // 队列的最大容量
    int max = 10;
    // 创建一个固定容量的数组来存储队列元素
    int[] arr;
    // 记录队列中已有元素的个数
    int num;
    // 队列头元素的下标,遵循先进先出原则
    int first;
    // 队列尾元素的下标,遵循后进后出原则
    int end;

    public MyBlockqueue(int max) {
        this.max = max;
        arr = new int[max];
    }

    // 模拟实现入队列操作
    public void put(int x) throws InterruptedException {
        // 检查队列是否已满
        while (num >= max) {
            // 如果已满,当前线程进入阻塞状态
            synchronized (this) {
                wait();
            }
        }

        // 队列未满,添加元素
        synchronized (this) {
            arr[end] = x;
            end = (end + 1) % max;
            num++;
            // 唤醒其他可能在等待的线程
            notify();
        }
    }

    // 模拟实现出队列操作
    public Integer take() throws InterruptedException {
        // 检查队列是否为空
        while (num == 0) {
            // 如果为空,当前线程进入阻塞状态
            synchronized (this) {
                wait();
            }
        }

        // 队列不为空,取出元素
        synchronized (this) {
            int tmp = arr[first];
            first = (first + 1) % max;
            num--;
            // 唤醒其他可能在等待的线程
            notify();
            return tmp;
        }
    }
}

接下来,我们使用这个自定义的阻塞队列来实现生产消费者模型:

import java.util.Random;

public class Main {
    public static void main(String[] args) {
        Random random = new Random();
        // 创建一个自定义的阻塞队列,容量为 10
        MyBlockqueue blockingDeque = new MyBlockqueue(10);

        // 创建生产者线程
        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    int value = random.nextInt(100);
                    System.out.println("生产元素:" + value);
                    blockingDeque.put(value);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        // 创建消费者线程
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    System.out.println("消费元素:" + blockingDeque.take());
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t1.start();
        t2.start();
    }
}

在这里插入图片描述

通过上述代码,我们成功地模拟实现了一个阻塞队列,并基于它构建了生产消费者模型,帮助我们更深入地理解阻塞队列的工作原理和应用场景。

希望这篇博客能让你对阻塞队列有更全面、深入的认识!

相关文章:

  • 第十六届蓝桥杯康复训练--8
  • 学习记录-vue2,3-vue实现tab栏
  • 齿轮啮合频率计算及其频谱图
  • [C语言基础] 第2章 算法的概念
  • kube-score K8S Yaml静态代码分析工具详解
  • 【申论】规范表达-科技创新类
  • 【Python自动化测试】——自动化测试基础
  • ABC392题解
  • 力扣算法ing(36 / 100)
  • 神经网络知识点整理
  • 中间件漏洞—Apache
  • 在线监测工具介绍 -- Arthas
  • 探索CSS3中那些不常用但特别强大的属性和属性值
  • JavaScript 比较运算符
  • 第二十章:类型属性的重载_《C++ Templates》notes
  • 【Go 】Go 语言中的 channel介绍
  • K8S学习之基础四十四:k8s中部署Kibana
  • vue3 ts 注册全局组件
  • N列股票收盘价为起点的马科维茨(Markowitz)均值—方差理论
  • 低配电脑畅玩《怪物猎人:荒野》,ToDesk云电脑优化从30帧到144帧?
  • 巴基斯坦外交部:印度侵略行径侵犯巴主权
  • 无人机穿越大理崇圣寺千年古塔时“炸机”,当地:肇事者已找到,将被追责
  • 中演协:五一假期全国营业性演出票房收入同比增长3.6%
  • 丁薛祥在学习《习近平经济文选》第一卷专题研讨班上强调:深入学习贯彻习近平经济思想,加强党中央对经济工作的集中统一领导
  • 黄道炫:南京102天——黄镇球的防空日记
  • 首日5金!中国队夺得跳水世界杯总决赛混合团体冠军