当前位置: 首页 > 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…...

2026年,IP地理位置精准查询的几个硬核技术变化

关于IP定位相关最近和几个同行交流&#xff0c;发现大家对IP定位的理解还停留在之前&#xff0c;想把自己这段时间的一些实践整理出来&#xff0c;希望能给同样在搞网络或风控的同行一些参考。 IPv6流量超过IPv4、住宅代理攻击泛滥、CGNAT覆盖越来越广……这些变化正在悄悄改变…...

1.2 struct page 与 PFN:VMA 背后的物理存储

本篇目标&#xff1a;理解 Linux 如何为每个物理页帧维护元数据&#xff08;struct page&#xff09;&#xff0c;以及虚拟地址最终如何落实到物理内存。HMM 的关键创新之一&#xff0c;是让设备内存&#xff08;GPU VRAM&#xff09;也拥有 struct page&#xff0c;从而被内核…...

别再装ModelSim了!用HDLBits网页版5分钟搞定Verilog仿真和波形图

5分钟极速验证&#xff1a;用HDLBits网页版替代传统Verilog仿真工具 在图书馆公用电脑上突然有了个FPGA设计灵感&#xff0c;却发现自己没装ModelSim&#xff1f;公司电脑没有管理员权限&#xff0c;无法安装Vivado Simulator&#xff1f;别急着放弃——打开浏览器&#xff0c…...

Pocket Sync:Analogue Pocket玩家的终极游戏管理解决方案

Pocket Sync&#xff1a;Analogue Pocket玩家的终极游戏管理解决方案 【免费下载链接】pocket-sync A GUI tool (Mac, Windows, Linux) for doing stuff with the Analogue Pocket 项目地址: https://gitcode.com/gh_mirrors/po/pocket-sync 想象一下&#xff0c;你刚刚…...

Static-Program-Analysis-Book中间表示解析:构建高效静态分析器的核心技术

Static-Program-Analysis-Book中间表示解析&#xff1a;构建高效静态分析器的核心技术 【免费下载链接】Static-Program-Analysis-Book Getting started with static program analysis. 静态程序分析入门教程。 项目地址: https://gitcode.com/gh_mirrors/st/Static-Program-…...

Sub-Zero字幕格式转换:从SRT到VTT的完整处理流程

Sub-Zero字幕格式转换&#xff1a;从SRT到VTT的完整处理流程 【免费下载链接】Sub-Zero.bundle Subtitles for Plex, as good you would expect them to be. 项目地址: https://gitcode.com/gh_mirrors/su/Sub-Zero.bundle Sub-Zero是一款为Plex媒体服务器提供高质量字幕…...

强制启动 Cursor IDE 主程序(不带 Agent 模式)

&#x1f527; 终极解决&#xff1a;强制启动 Cursor IDE 主程序&#xff08;不带 Agent 模式&#xff09;方法 1&#xff1a;用「命令行」强制启动主程序&#xff08;最稳&#xff09;按 WinR 打开运行窗口&#xff0c;输入 cmd 回车&#xff0c;打开命令提示符输入下面这行命…...

机器学习论文有效阅读:三层穿透法定位技术杠杆点

1. 这不是“读论文”&#xff0c;而是“拆解模型生长的土壤”你有没有过这种体验&#xff1a;打开一篇顶会论文&#xff0c;标题写着《Neural Architecture Search with Reinforcement Learning》&#xff0c;摘要读得热血沸腾&#xff0c;结果翻到Methodology部分&#xff0c;…...

GitHub 被黑或因员工安装 Nx Console 恶意扩展引发,更多详情待调查

聚焦源代码安全&#xff0c;网罗国内外最新资讯&#xff01; 编译&#xff1a;代码卫士专栏供应链安全数字化时代&#xff0c;软件无处不在。软件如同社会中的“虚拟人”&#xff0c;已经成为支撑社会正常运转的最基本元素之一&#xff0c;软件的安全性问题也正在成为当今社会的…...

LLM处理半结构化数据,csv数据 :在序列化层对字段按熵分层路由——把每个低熵层一次性全局总结、把高熵 TEXT 用“质心+样例“做率最优覆盖、把寻址 α 显式落进 prompt

怎么给LLM 总结结论进行溯源 先搞清「寻址函数 α」是什么 L3 / L4 已经把 12 万条文本压成 8 类模式 + 几条原话证据。可这时候 LLM 看到的只是抽象论断: 「机型 X1C 的喷头堵塞,主要原因是耗材含水(占该类 18%)」 分析师马上会追问:“这 18% 具体是哪 5,200 条工单?给…...