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

实现阻塞队列

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class Main  {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue = new LinkedBlockingQueue<>();
        queue.put("111");
        queue.put("222");
        queue.put("333");
        queue.put("444");
        String elem = queue.take();
        System.out.println(elem);
        elem = queue.take();
        System.out.println(elem);
        elem = queue.take();
        System.out.println(elem);
        elem = queue.take();
        System.out.println(elem);
        elem = queue.take();
        System.out.println(elem);
    }
}

// 直接让这个队列里面存储字符串.
class MyBlockingQueue {
    // 此处这里的最大长度, 也可以指定构造方法, 由构造方法的参数来制定.
    private String[] data = new String[1000];
    // 队列的起始位置.
    private volatile int head = 0;
    // 队列的结束位置的下一个位置.
    private volatile int tail = 0;
    // 队列中有效元素的个数.
    private volatile int size = 0;

    // private final Object locker = new Object();

    // 提供核心方法, 入队列和出队列.
    public void put(String elem) throws InterruptedException {
        synchronized (this) {
            while (size == data.length) {
                // 队列满了.
                // 如果是队列满, 继续插入元素, 就会阻塞.
                this.wait();
            }
            // 队列没满, 真正的往里面添加元素
            data[tail] = elem;
            tail++;
            // 如果 tail 自增之后, 到达了数组末尾. 这个时候就需要让它回到开头 (环形队列)
            if (tail == data.length) {
                tail = 0;
            }
            size++;
            // 这个 notify 用来唤醒 take 中的 wait
            this.notify();
        }
    }

    public String take() throws InterruptedException {
        synchronized (this) {
            while (size == 0) {
                // 队列空了.
                this.wait();
            }
            // 队列不空, 就可以把队首元素 (head 位置的元素) 删除掉, 并进行返回.
            String ret = data[head];
            head++;
            if (head == data.length) {
                head = 0;
            }
            size--;
            // 这个 notify 用来唤醒 put 中的 wait
            this.notify();
            return ret;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        // 生产者, 消费者, 分别使用一个线程表示. (也可以使用多个线程)

        MyBlockingQueue queue = new MyBlockingQueue();

        // 消费者
        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    String result = queue.take();
                    System.out.println("消费元素: " + result);

                    // 暂时先不 sleep
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        // 生产者
        Thread t2 = new Thread(() -> {
            int num = 1;
            while (true) {
                try {
                    queue.put(num + "");
                    System.out.println("生产元素: " + num);
                    num++;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

        t1.start();
        t2.start();
    }
}
import java.util.Timer;
import java.util.TimerTask;

// 定时器
public class Main{
    public static void main(String[] args) {
        Timer timer = new Timer();
        // 给定时器安排了一个任务, 预定在 xxx 时间去执行.
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("3000");
            }
        }, 3000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("2000");
            }
        }, 2000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("1000");
            }
        }, 1000);
        System.out.println("程序启动!");
    }
}

import java.util.PriorityQueue;

// 通过这个类, 描述了一个任务
class MyTimerTask implements Comparable<MyTimerTask> {
    // 要有一个要执行的任务
    private Runnable runnable;
    // 还要有一个执行任务的时间
    private long time;

    // 此处的 delay 就是 schedule 方法传入的 "相对时间"
    public MyTimerTask(Runnable runnable, long delay) {
        this.runnable = runnable;
        this.time = System.currentTimeMillis() + delay;
    }

    @Override
    public int compareTo(MyTimerTask o) {
        // 这样的写法, 就是让队首元素是最小时间的值
        // 到底是谁 - 谁, 不要背!! 你可以试试!!
        return (int) (this.time - o.time);
        // 如果是想让队首元素是最大时间的值
        // return o.time - this.time;
    }

    public long getTime() {
        return time;
    }

    public Runnable getRunnable() {
        return runnable;
    }
}

// 自己搞的定时器
class MyTimer {
    // 使用一个数据结构, 保存所有要安排的任务.
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();
    // 使用这个对象作为锁对象.
    private Object locker = new Object();

    public void schedule(Runnable runnable, long delay) {
        synchronized (locker) {
            queue.offer(new MyTimerTask(runnable, delay));
            locker.notify();
        }
    }

    // 搞个扫描线程.
    public MyTimer() {
        // 创建一个扫描线程
        Thread t = new Thread(() -> {
            // 扫描线程, 需要不停的扫描队首元素, 看是否是到达时间.
            while (true) {
                try {
                    synchronized (locker) {
                        // 不要使用 if 作为 wait 的判定条件, 应该使用 while
                        // 使用 while 的目的是为了在 wait 被唤醒的时候, 再次确认一下条件.
                        while (queue.isEmpty()) {
                            // 使用 wait 进行等待.
                            // 这里的 wait, 需要由另外的线程唤醒.
                            // 添加了新的任务, 就应该唤醒.
                            locker.wait();
                        }
                        MyTimerTask task = queue.peek();
                        // 比较一下看当前的队首元素是否可以执行了.
                        long curTime = System.currentTimeMillis();
                        if (curTime >= task.getTime()) {
                            // 当前时间已经达到了任务时间, 就可以执行任务了
                            task.getRunnable().run();
                            // 任务执行完了, 就可以从队列中删除了.
                            queue.poll();
                        } else {
                            // 当前时间还没到任务时间, 暂时不执行任务.
                            // 暂时先啥都不干, 等待下一轮的循环判定了.
                            locker.wait(task.getTime() - curTime);
                        }
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        t.start();
    }
}

public class Main {
    public static void main(String[] args) {
        MyTimer timer = new MyTimer();
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("3000");
            }
        }, 3000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("2000");
            }
        }, 2000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("1000");
            }
        }, 1000);
        System.out.println("程序开始执行");
    }
}
 

相关文章:

实现阻塞队列

import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; public class Main  { public static void main(String[] args) throws InterruptedException { BlockingQue…...

MySQL8.X驱动datetime映射问题

MySQL8.X驱动datetime映射问题 背景&#xff1a;项目由SSM项目迁移至SpringBoot&#xff0c;对mysql数据库驱动进行了升级导致出现问题。 原因&#xff1a;mysql驱动的8.X版本对数据库类型datetime映射为LocalDateTime。 解决&#xff1a;暂时不升级mysql驱动&#xff0c;mys…...

【Selenium】隐藏元素的定位和操作|隐藏与isDisplay方法

一、selenium 中隐藏元素如何定位&#xff1f; 如果单纯的定位的话&#xff0c;隐藏元素和普通不隐藏元素定位没啥区别&#xff0c;用正常定位方法就行了 但是吧~~~能定位到并不意味着能操作元素&#xff08;如click,clear,send_keys&#xff09; 二、隐藏元素 如下图有个输入框…...

视图的作用

目录 视图的作用 创建视图 为 scott 分配创建视图的权限 查询视图 复杂视图的创建 视图更新的限制问题 更新视图中数据的部门编号&#xff08;视图的存在条件&#xff09; 限制通过视图修改数据表内容 创建只读的视图 复杂视图创建 oracle从入门到总裁:​​​​​​h…...

动态ip白名单频繁更改问题解决方案

1. 使用静态IP地址&#xff1a;可以通过向ISP申请静态IP地址来解决动态IP地址的变化问题&#xff0c;但是这种方法会比较贵。 2. 使用动态DNS&#xff1a;可以使用动态DNS服务&#xff0c;它可以将动态IP地址映射到一个固定的域名&#xff0c;从而使得动态IP地址处理为域名一直…...

什么是物联网监控平台?部署物联网平台有什么作用?

随着物联网技术的飞速发展&#xff0c;越来越多的企业开始关注并投入到这一领域。物联网监控平台作为连接物理世界与数字世界的桥梁&#xff0c;正逐渐成为企业数字化转型的关键组件。今天&#xff0c;我们将深入探讨物联网监控平台的定义、部署物联网平台的作用&#xff0c;以…...

netty构建udp服务器以及发送报文到客户端客户端详细案例

目录 一、基于netty创建udp服务端以及对应通道设置关键 二、发送数据 三、netty中的ChannelOption常用参数说明 1、ChannelOption.SO_BACKLOG 2、ChannelOption.SO_REUSEADDR 3、ChannelOption.SO_KEEPALIVE 4、ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF 5、Ch…...

Selenium 学习(0.22)——软件测试之小结

Junit 等一系列自动化工具不用啰嗦了&#xff0c;自己就是小白再搞科普就装了。 把后面相关内容看了一下&#xff0c;使用这些测试工具一样编写代码&#xff08;驱动模块【调用接口的代码片段】&#xff0c;桩模块【响应输出结果的代码片段&#xff0c;也就是被测模块调用的模块…...

贪心算法问题

分发饼干-455 假设你是一位很棒的家长&#xff0c;想要给你的孩子们一些小饼干。但是&#xff0c;每个孩子最多只能给一块饼干。对每个孩子 i &#xff0c;都有一个胃口值 gi &#xff0c;这是能让孩子们满足胃口的饼干的最小尺寸&#xff1b;并且每块饼干 j &#xff0c;都有…...

深入理解 @Transactional 注解在 Spring 中的应用

前言&#xff1a;在 Java 开发中&#xff0c;事务管理是非常重要的一环。Spring 框架提供了Transactional注解来简化事务管理的操作&#xff0c;本文将深入介绍Transactional注解的用法&#xff0c;并结合代码示例进行详细讨论。 1.Transactional 注解简介 Transactional注解是…...

Python爬虫之爬取网页图片

当我们想要下载网页的图片时&#xff0c;发现网页的图片太多了&#xff0c;无从下手&#xff0c;那我们写一个脚本来爬取呗。 这次的脚本是专门针对某个外国网站使用的&#xff0c;因此仅供参考思路。 在测试的过程中&#xff0c;我发现网站使用了发爬虫机制&#xff0c;具体就…...

AI Agent(LLM Agent)入门解读

1. 什么是AI Agent&#xff1f; AI Agent可以理解为一个智能体&#xff0c;包括感知模块、规划决策模块和行动模块&#xff0c;类似于人类的五官、大脑和肢体。它能帮助人类处理复杂的任务&#xff0c;并能根据环境反馈进行学习和调整。 五官可以理解为感知模块&#xff0c;大…...

自动化面试常见算法题!

1、实现一个数字的反转&#xff0c;比如输入12345&#xff0c;输出54321 num 12345 num_str str(num) reversed_num_str num_str[::-1] reversed_num int(reversed_num_str) print(reversed_num) # 输出 54321代码解析&#xff1a;首先将输入的数字转换为字符串&#xff…...

CCF-CSP真题202206-2《寻宝!大冒险!》

题目背景 暑假要到了。可惜由于种种原因&#xff0c;小 P 原本的出游计划取消。失望的小 P 只能留在西西艾弗岛上度过一个略显单调的假期……直到…… 某天&#xff0c;小 P 获得了一张神秘的藏宝图。 问题描述 西西艾弗岛上种有 n 棵树&#xff0c;这些树的具体位置记录在…...

Rust编程(三)生命周期与异常处理

生命周期 生命周期&#xff0c;简而言之就是引用的有效作用域。在大多数时候&#xff0c;我们无需手动的声明生命周期&#xff0c;因为编译器可以自动进行推导。生命周期的主要作用是避免悬垂引用&#xff0c;它会导致程序引用了本不该引用的数据&#xff1a; {let r;{let x …...

【办公类-21-11】 20240327三级育婴师 多个二级文件夹的docx合并成docx有页码,转PDF

背景展示&#xff1a;有页码的操作题 背景需求&#xff1a; 实操课终于全部结束了&#xff0c;把考试内容&#xff08;docx&#xff09;都写好了 【办公类-21-10】三级育婴师 视频转文字docx&#xff08;等线小五单倍行距&#xff09;&#xff0c;批量改成“宋体小四、1.5倍行…...

OSG编程指南<二十一>:OSG视图与相机视点更新设置及OSG宽屏变形

1、概述 什么是视图?在《OpenGL 编程指南》中有下面的比喻,从笔者开始学习图形学就影响深刻,相信对读者学习场景管理也会非常有帮助。 产生目标场景视图的变换过程类似于用相机进行拍照,主要有如下的步骤: (1)把照相机固定在三脚架上,让它对准场景(视图变换)。 (2)…...

Laplace变换-3

回忆#常见函数的Laplace变换&#xff1a; t z − 1 ↦ Γ ( z ) s z t^{z-1} \mapsto \frac{\Gamma(z)}{s^{z}} tz−1↦szΓ(z)​ &#xff08;要求 R e ( z ) > 0 \mathrm{Re}(z)>0 Re(z)>0&#xff09; e a t ↦ 1 s − a e^{at} \mapsto \frac{1}{s-a} eat↦s−a1​…...

LVS负载均衡-DR模式配置

LVS&#xff1a;Linux virtual server ,即Linux虚拟服务器 LVS自身是一个负载均衡器&#xff08;Director&#xff09;&#xff0c;不直接处理请求&#xff0c;而是将请求转发至位于它后端的真实服务器real server上。 LVS是四层&#xff08;传输层 tcp/udp&#xff09;负载均衡…...

【unity】如何汉化unity Hub

相信大家下载安装unity后看着满操作栏的英文&#xff0c;英文不好的小伙伴们会一头雾水。但是没关系你要记住你要怎么高速运转的机器进入中国&#xff0c;请记住我给出的原理&#xff0c;不懂不代表不会用啊。现在我们就来把编译器给进行汉化。 第一步&#xff1a;我们打开Uni…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

QMC5883L的驱动

简介 本篇文章的代码已经上传到了github上面&#xff0c;开源代码 作为一个电子罗盘模块&#xff0c;我们可以通过I2C从中获取偏航角yaw&#xff0c;相对于六轴陀螺仪的yaw&#xff0c;qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

Xen Server服务器释放磁盘空间

disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...

在golang中如何将已安装的依赖降级处理,比如:将 go-ansible/v2@v2.2.0 更换为 go-ansible/@v1.1.7

在 Go 项目中降级 go-ansible 从 v2.2.0 到 v1.1.7 具体步骤&#xff1a; 第一步&#xff1a; 修改 go.mod 文件 // 原 v2 版本声明 require github.com/apenella/go-ansible/v2 v2.2.0 替换为&#xff1a; // 改为 v…...