数据结构与算法-阻塞队列

单锁实现

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class BlockingQueue1<E> implements BlockingQueue<E> {
    private final E[] array; // 内部存储元素的数组
    private int head = 0;    // 队列头部索引
    private int tail = 0;    // 队列尾部索引
    private int size = 0;    // 队列中元素的数量

    /**
     * 构造函数,初始化队列容量
     *
     * @param capacity 队列的最大容量
     */
    @SuppressWarnings("all")
    public BlockingQueue1(int capacity) {
        array = (E[]) new Object[capacity]; // 创建一个固定大小的数组
    }

    private final ReentrantLock lock = new ReentrantLock(); // 互斥锁,用于控制并发访问
    private final Condition tailWaits = lock.newCondition(); // 等待队列满的条件变量
    private final Condition headWaits = lock.newCondition(); // 等待队列空的条件变量

    /**
     * 将元素插入队列末尾,如果队列已满则等待直到有空间可用
     *
     * @param e 要插入的元素
     * @throws InterruptedException 如果当前线程在等待时被中断
     */
    @Override
    public void offer(E e) throws InterruptedException {
        lock.lockInterruptibly(); // 获取锁,并允许响应中断
        try {
            while (isFull()) { // 如果队列已满,则等待
                tailWaits.await();
            }
            array[tail] = e; // 插入元素到队列尾部
            if (++tail == array.length) { // 更新尾部指针,循环队列处理
                tail = 0;
            }
            size++; // 增加元素计数
            headWaits.signal(); // 通知可能等待的消费者线程
        } finally {
            lock.unlock(); // 确保无论是否发生异常都释放锁
        }
    }

    /**
     * 将元素插入队列末尾,如果队列已满且超过指定超时时间则放弃插入
     *
     * @param e       要插入的元素
     * @param timeout 超时时间,单位为毫秒
     * @throws InterruptedException 如果当前线程在等待时被中断
     */
    @Override
    public void offer(E e, long timeout) throws InterruptedException {
        lock.lockInterruptibly(); // 获取锁,并允许响应中断
        try {
            long t = TimeUnit.MILLISECONDS.toNanos(timeout); // 将毫秒转换为纳秒
            while (isFull()) { // 如果队列已满,则等待
                if (t <= 0) { // 如果超时时间已到,返回
                    return;
                }
                t = tailWaits.awaitNanos(t); // 等待并更新剩余时间
            }
            array[tail] = e; // 插入元素到队列尾部
            if (++tail == array.length) { // 更新尾部指针,循环队列处理
                tail = 0;
            }
            size++; // 增加元素计数
            headWaits.signal(); // 通知可能等待的消费者线程
        } finally {
            lock.unlock(); // 确保无论是否发生异常都释放锁
        }
    }

    /**
     * 从队列头部移除并返回元素,如果队列为空则等待直到有元素可用
     *
     * @return 队列头部的元素
     * @throws InterruptedException 如果当前线程在等待时被中断
     */
    @Override
    public E poll() throws InterruptedException {
        lock.lockInterruptibly(); // 获取锁,并允许响应中断
        try {
            while (isEmpty()) { // 如果队列为空,则等待
                headWaits.await();
            }
            E e = array[head]; // 获取队列头部的元素
            array[head] = null; // 清除引用以帮助垃圾回收
            if (++head == array.length) { // 更新头部指针,循环队列处理
                head = 0;
            }
            size--; // 减少元素计数
            tailWaits.signal(); // 通知可能等待的生产者线程
            return e;
        } finally {
            lock.unlock(); // 确保无论是否发生异常都释放锁
        }
    }

    /**
     * 检查队列是否为空
     *
     * @return 如果队列为空则返回 true,否则返回 false
     */
    private boolean isEmpty() {
        return size == 0;
    }

    /**
     * 检查队列是否已满
     *
     * @return 如果队列已满则返回 true,否则返回 false
     */
    private boolean isFull() {
        return size == array.length;
    }
}

双锁实现

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.atomic.AtomicInteger;

public class BlockingQueue2<E> implements BlockingQueue<E> {

    private final E[] array; // 内部存储元素的数组
    private int head = 0;    // 队列头部索引
    private int tail = 0;    // 队列尾部索引
    private final AtomicInteger size = new AtomicInteger(0); // 使用原子整数来保证线程安全的计数器

    // 控制对队列尾部的访问
    private final ReentrantLock tailLock = new ReentrantLock();
    private final Condition tailWaits = tailLock.newCondition(); // 等待队列满的条件变量

    // 控制对队列头部的访问
    private final ReentrantLock headLock = new ReentrantLock();
    private final Condition headWaits = headLock.newCondition(); // 等待队列空的条件变量

    /**
     * 构造函数,初始化队列容量
     *
     * @param capacity 队列的最大容量
     */
    public BlockingQueue2(int capacity) {
        this.array = (E[]) new Object[capacity]; // 创建一个固定大小的数组
    }

    /**
     * 将元素插入队列末尾,如果队列已满则等待直到有空间可用
     *
     * @param e 要插入的元素
     * @throws InterruptedException 如果当前线程在等待时被中断
     */
    @Override
    public void offer(E e) throws InterruptedException {
        int c; // 用于保存当前队列中的元素数量
        tailLock.lockInterruptibly(); // 获取尾部锁,并允许响应中断
        try {
            while (isFull()) { // 如果队列已满,则等待
                tailWaits.await();
            }
            array[tail] = e; // 插入元素到队列尾部
            if (++tail == array.length) { // 更新尾部指针,循环队列处理
                tail = 0;
            }
            c = size.getAndIncrement(); // 增加元素计数,并获取之前的值
            // a. 队列不满, 但不是从满->不满, 由此offer线程唤醒其它offer线程
            if (c + 1 < array.length) {
                tailWaits.signal(); // 唤醒其他可能等待的生产者线程
            }
        } finally {
            tailLock.unlock(); // 确保无论是否发生异常都释放锁
        }
        // b. 从0->不空, 由此offer线程唤醒等待的poll线程
        if (c == 0) {
            headLock.lock(); // 获取头部锁
            try {
                headWaits.signal(); // 唤醒可能等待的消费者线程
            } finally {
                headLock.unlock(); // 确保无论是否发生异常都释放锁
            }
        }
    }

    /**
     * 从队列头部移除并返回元素,如果队列为空则等待直到有元素可用
     *
     * @return 队列头部的元素
     * @throws InterruptedException 如果当前线程在等待时被中断
     */
    @Override
    public E poll() throws InterruptedException {
        E e; // 用于保存出队的元素
        int c; // 用于保存当前队列中的元素数量
        headLock.lockInterruptibly(); // 获取头部锁,并允许响应中断
        try {
            while (isEmpty()) { // 如果队列为空,则等待
                headWaits.await();
            }
            e = array[head]; // 获取队列头部的元素
            array[head] = null; // 清除引用以帮助垃圾回收
            if (++head == array.length) { // 更新头部指针,循环队列处理
                head = 0;
            }
            c = size.getAndDecrement(); // 减少元素计数,并获取之前的值
            // b. 队列不空, 但不是从0变化到不空,由此poll线程通知其它poll线程
            if (c > 1) {
                headWaits.signal(); // 唤醒其他可能等待的消费者线程
            }
        } finally {
            headLock.unlock(); // 确保无论是否发生异常都释放锁
        }
        // a. 从满->不满, 由此poll线程唤醒等待的offer线程
        if (c == array.length) {
            tailLock.lock(); // 获取尾部锁
            try {
                tailWaits.signal(); // 唤醒可能等待的生产者线程
            } finally {
                tailLock.unlock(); // 确保无论是否发生异常都释放锁
            }
        }
        return e;
    }

    /**
     * 检查队列是否为空
     *
     * @return 如果队列为空则返回 true,否则返回 false
     */
    private boolean isEmpty() {
        return size.get() == 0;
    }

    /**
     * 检查队列是否已满
     *
     * @return 如果队列已满则返回 true,否则返回 false
     */
    private boolean isFull() {
        return size.get() == array.length;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/972019.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

部署k8s 集群1.26.0(containerd方式)

随着k8s版本逐步更新&#xff0c;在不支持docker环境的情况下&#xff0c;需要使用containerd方式作为容器引擎。为了更好的个人学习使用&#xff0c;需要重新部署一套1.26.0版本的k8s集群&#xff0c;并且使用containerd方式作为容器引擎&#xff0c;版本为1.6.33。在部署过程…

移动通信发展史

概念解释 第一代网络通信 1G 第二代网络通信 2G 第三代网络通信 3G 第四代网络通信 4G 4g网络有很高的速率和很低的延时——高到500M的上传和1G的下载 日常中的4G只是用到了4G技术 运营商 移动-从民企到国企 联通-南方教育口有人 电信 铁通&#xff1a;成立于 2000 年…

HarmonyOS进程通信及原理

大家好&#xff0c;我是学徒小z&#xff0c;最近在研究鸿蒙中一些偏底层原理的内容&#xff0c;今天分析进程通信给大家&#xff0c;请用餐&#x1f60a; 文章目录 进程间通信1. 通过公共事件&#xff08;ohos.commonEventManager&#xff09;公共事件的底层原理 2. IPC Kit能…

openCV中如何实现滤波

图像滤波用于去除噪声和图像平滑&#xff0c;OpenCV 提供了多种滤波器&#xff1a; 1.1. 均值滤波&#xff1a; import cv2# 读取图像 image cv2.imread("example.jpg")# 均值滤波 blurred_image cv2.blur(image, (5, 5)) # (5, 5) 是滤波核的大小 滤波核大小的…

Linux网络 | 多路转接Reactor

前言&#xff1a;本节内容结束Linux网络部分。本节将要简单实现一下多路转接Reactor的代码&#xff0c;制作一个多路转接版本的四则运算计算器服务器。Reactor的代码相当困难&#xff0c;除了350多行新代码&#xff0c; 还要用到我们之前写的许多文件&#xff0c; 比如之前写的…

数控机床设备分布式健康监测与智能维护系统MTAgent

数控机床设备分布式健康监测与智能维护系统MTAgent-v1.1融合了目前各种先进的信号处理以及信息分析算法以算法工具箱的方式&#xff0c;采用了一种开发的、模块化的结构实现信号各种分析处理&#xff0c;采用Python编程语言&#xff0c;满足不同平台需求(包括Windows、Linux)。…

Opencv项目实战:26 信用卡号码识别与类型判定

项目介绍 在日常生活中&#xff0c;信用卡的使用越来越普遍。本项目的主要目标是通过图像处理技术自动识别信用卡号码&#xff0c;并根据信用卡号码的第一个数字判定信用卡的类型&#xff08;如Visa、MasterCard等&#xff09;。项目结合了图像预处理、轮廓检测、模板匹配等技…

利用websocket检测网络连接稳定性

浏览器中打开F12&#xff0c;控制台中输入以下内容 > 回车 > 等待结果 连接关闭 表示断网 let reconnectDelay 1000; // 初始重连间隔 let pingInterval null; let socketManuallyClosed false; // 标志是否手动关闭function createWebSocket() {if (socketManuallyCl…

WPF9-数据绑定进阶

目录 1. 定义2. 背景3. Binding源3.1. 使用Data Context作为Binding的源3.2. 使用LINQ检索结果作为Binding的源 4. Binding对数据的转换和校验4.1. 需求4.2. 实现步骤4.3. 值转换和校验的好处4.3.1. 数据转换的好处 4.4. 数据校验的好处4.5. 原理4.5.1. 值转换器原理4.5.2. 数据…

【Unity Shader编程】之图元装配与光栅化

执行方式&#xff1a;自动完成 图元装配自动化流程 顶点坐标存入装配区 → 按绘制模式连接顶点 → 生成完整几何图元 示例&#xff1a;gl.drawArrays(gl.TRIANGLES, 0, 3)自动生成三角形 会自动自动裁剪超出屏幕范围&#xff08;NDC空间外&#xff09;的三角形&#xff0c;仅保…

ssm121基于ssm的开放式教学评价管理系统+vue(源码+包运行+LW+技术指导)

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

网工项目理论1.11 网络出口设计

本专栏持续更新&#xff0c;整一个专栏为一个大型复杂网络工程项目。阅读本文章之前务必先看《本专栏必读》。 一.网络出口接入技术 二.单一出口网络结构 三.同运营商多出口结构 四.多运营商多出口结构——出向流量 五.多运营商多出口结构——服务器访问流量 六.多运营商多出口…

Django 5 实用指南(一)安装与配置

1.1 Django5的背景与发展 Django 自从2005年由Adrian Holovaty和Simon Willison在 Lawrence Journal-World 新闻网站上首次发布以来&#xff0c;Django 一直是 Web 开发领域最受欢迎的框架之一。Django 框架经历了多个版本的演进&#xff0c;每次版本更新都引入了新功能、改进了…

Redis实战-扩展Redis

扩展Redis 1、扩展读性能2、扩展写性能和内存容量3、扩展复杂的查询3.1 扩展联合查询3.2 扩展分片排序 如有侵权&#xff0c;请联系&#xff5e; 如有错误&#xff0c;也欢迎批评指正&#xff5e; 本篇文章大部分是来自学习《Redis实战》的笔记 1、扩展读性能 单台Redis服务器…

【AI面板识别】

题目描述 AI识别到面板上有N&#xff08;1 ≤ N ≤ 100&#xff09;个指示灯&#xff0c;灯大小一样&#xff0c;任意两个之间无重叠。 由于AI识别误差&#xff0c;每次别到的指示灯位置可能有差异&#xff0c;以4个坐标值描述AI识别的指示灯的大小和位置(左上角x1,y1&#x…

朴素模式匹配算法与KMP算法(有next[]和nextval[]详细讲解

这篇文章是建立在上篇文章的基础上的,看此篇文章要有串的基本知识 举个例子引进我们今天的知识 假设我们这里有两个字符串,一个主串,一个子串 主串: aaa223aa225 子串: aa22 我们这里需要进行匹配,传统的朴素模式匹配算法,就是主串下标i从1开始,主串j从1开始…

文件操作(PHP)(小迪网络安全笔记~

免责声明&#xff1a;本文章仅用于交流学习&#xff0c;因文章内容而产生的任何违法&未授权行为&#xff0c;与文章作者无关&#xff01;&#xff01;&#xff01; 附&#xff1a;完整笔记目录~ ps&#xff1a;本人小白&#xff0c;笔记均在个人理解基础上整理&#xff0c;…

【分治法】棋盘覆盖问题 C/C++(附代码和测试实例及算法分析)

问题描述 在一个 2 k 2 k 2^k \times 2^k 2k2k大小的棋盘中&#xff0c;有一个与其他方块不同的特殊方块&#xff0c;如下图红色方块。另有4种形态不同的L型骨块&#xff08;见下图&#xff09;&#xff0c;要用图示四种骨块覆盖棋盘上除特殊方格外的其他所有方格&#xff0c…

el-table的hasChildren不生效?子级没数据还显示箭头号?树形数据无法展开和收缩

问题&#xff1a;明明子级只有一条数据&#xff0c;还显示箭头号 原因&#xff1a;最开始row-key写的是id,父级和子级都有该属性&#xff0c;所以展开失效了。 解决方法&#xff1a;row-key&#xff1a;id改成 row-key"name"

2002-2019年各省人口老龄化程度数据

2002-2019年各省人口老龄化程度数据 1、时间&#xff1a;2002-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;地区、年度、六十五岁以上占比 4、范围&#xff1a;31省 5、指标解释&#xff1a;人口老龄化是指人口生育率降低和人均寿命延长导致的总人…