实现阻塞队列
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映射问题 背景:项目由SSM项目迁移至SpringBoot,对mysql数据库驱动进行了升级导致出现问题。 原因:mysql驱动的8.X版本对数据库类型datetime映射为LocalDateTime。 解决:暂时不升级mysql驱动,mys…...
【Selenium】隐藏元素的定位和操作|隐藏与isDisplay方法
一、selenium 中隐藏元素如何定位? 如果单纯的定位的话,隐藏元素和普通不隐藏元素定位没啥区别,用正常定位方法就行了 但是吧~~~能定位到并不意味着能操作元素(如click,clear,send_keys) 二、隐藏元素 如下图有个输入框…...
视图的作用
目录 视图的作用 创建视图 为 scott 分配创建视图的权限 查询视图 复杂视图的创建 视图更新的限制问题 更新视图中数据的部门编号(视图的存在条件) 限制通过视图修改数据表内容 创建只读的视图 复杂视图创建 oracle从入门到总裁:h…...
动态ip白名单频繁更改问题解决方案
1. 使用静态IP地址:可以通过向ISP申请静态IP地址来解决动态IP地址的变化问题,但是这种方法会比较贵。 2. 使用动态DNS:可以使用动态DNS服务,它可以将动态IP地址映射到一个固定的域名,从而使得动态IP地址处理为域名一直…...
什么是物联网监控平台?部署物联网平台有什么作用?
随着物联网技术的飞速发展,越来越多的企业开始关注并投入到这一领域。物联网监控平台作为连接物理世界与数字世界的桥梁,正逐渐成为企业数字化转型的关键组件。今天,我们将深入探讨物联网监控平台的定义、部署物联网平台的作用,以…...
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 等一系列自动化工具不用啰嗦了,自己就是小白再搞科普就装了。 把后面相关内容看了一下,使用这些测试工具一样编写代码(驱动模块【调用接口的代码片段】,桩模块【响应输出结果的代码片段,也就是被测模块调用的模块…...
贪心算法问题
分发饼干-455 假设你是一位很棒的家长,想要给你的孩子们一些小饼干。但是,每个孩子最多只能给一块饼干。对每个孩子 i ,都有一个胃口值 gi ,这是能让孩子们满足胃口的饼干的最小尺寸;并且每块饼干 j ,都有…...
深入理解 @Transactional 注解在 Spring 中的应用
前言:在 Java 开发中,事务管理是非常重要的一环。Spring 框架提供了Transactional注解来简化事务管理的操作,本文将深入介绍Transactional注解的用法,并结合代码示例进行详细讨论。 1.Transactional 注解简介 Transactional注解是…...
Python爬虫之爬取网页图片
当我们想要下载网页的图片时,发现网页的图片太多了,无从下手,那我们写一个脚本来爬取呗。 这次的脚本是专门针对某个外国网站使用的,因此仅供参考思路。 在测试的过程中,我发现网站使用了发爬虫机制,具体就…...
AI Agent(LLM Agent)入门解读
1. 什么是AI Agent? AI Agent可以理解为一个智能体,包括感知模块、规划决策模块和行动模块,类似于人类的五官、大脑和肢体。它能帮助人类处理复杂的任务,并能根据环境反馈进行学习和调整。 五官可以理解为感知模块,大…...
自动化面试常见算法题!
1、实现一个数字的反转,比如输入12345,输出54321 num 12345 num_str str(num) reversed_num_str num_str[::-1] reversed_num int(reversed_num_str) print(reversed_num) # 输出 54321代码解析:首先将输入的数字转换为字符串ÿ…...
CCF-CSP真题202206-2《寻宝!大冒险!》
题目背景 暑假要到了。可惜由于种种原因,小 P 原本的出游计划取消。失望的小 P 只能留在西西艾弗岛上度过一个略显单调的假期……直到…… 某天,小 P 获得了一张神秘的藏宝图。 问题描述 西西艾弗岛上种有 n 棵树,这些树的具体位置记录在…...
Rust编程(三)生命周期与异常处理
生命周期 生命周期,简而言之就是引用的有效作用域。在大多数时候,我们无需手动的声明生命周期,因为编译器可以自动进行推导。生命周期的主要作用是避免悬垂引用,它会导致程序引用了本不该引用的数据: {let r;{let x …...
【办公类-21-11】 20240327三级育婴师 多个二级文件夹的docx合并成docx有页码,转PDF
背景展示:有页码的操作题 背景需求: 实操课终于全部结束了,把考试内容(docx)都写好了 【办公类-21-10】三级育婴师 视频转文字docx(等线小五单倍行距),批量改成“宋体小四、1.5倍行…...
OSG编程指南<二十一>:OSG视图与相机视点更新设置及OSG宽屏变形
1、概述 什么是视图?在《OpenGL 编程指南》中有下面的比喻,从笔者开始学习图形学就影响深刻,相信对读者学习场景管理也会非常有帮助。 产生目标场景视图的变换过程类似于用相机进行拍照,主要有如下的步骤: (1)把照相机固定在三脚架上,让它对准场景(视图变换)。 (2)…...
Laplace变换-3
回忆#常见函数的Laplace变换: t z − 1 ↦ Γ ( z ) s z t^{z-1} \mapsto \frac{\Gamma(z)}{s^{z}} tz−1↦szΓ(z) (要求 R e ( z ) > 0 \mathrm{Re}(z)>0 Re(z)>0) e a t ↦ 1 s − a e^{at} \mapsto \frac{1}{s-a} eat↦s−a1…...
LVS负载均衡-DR模式配置
LVS:Linux virtual server ,即Linux虚拟服务器 LVS自身是一个负载均衡器(Director),不直接处理请求,而是将请求转发至位于它后端的真实服务器real server上。 LVS是四层(传输层 tcp/udp)负载均衡…...
【unity】如何汉化unity Hub
相信大家下载安装unity后看着满操作栏的英文,英文不好的小伙伴们会一头雾水。但是没关系你要记住你要怎么高速运转的机器进入中国,请记住我给出的原理,不懂不代表不会用啊。现在我们就来把编译器给进行汉化。 第一步:我们打开Uni…...
Lumerical圆锥建模避坑指南:从参数计算到3D旋转生成的完整流程
Lumerical圆锥建模避坑指南:从参数计算到3D旋转生成的完整流程 在光学仿真领域,精确的几何建模往往是获得可靠结果的第一步。对于圆锥结构这种在光子晶体、超表面和纳米天线设计中广泛应用的形状,其建模过程看似简单却暗藏玄机。许多研究人员…...
Pikachu靶场实战:SQL注入漏洞深度解析与防御指南
1. SQL注入漏洞初探:从Pikachu靶场开始 第一次接触SQL注入时,我完全被这种"通过输入框就能控制数据库"的神奇攻击方式震惊了。在Pikachu靶场这个专为Web安全学习设计的实验环境中,我们可以安全地体验各种SQL注入攻击手法。不同于真…...
告别重复造轮子,用快马为openclaw项目生成高效通用解析器提升开发效率
最近在维护一个叫openclaw的开源爬虫项目时,发现每次遇到新网站都要重写解析逻辑,不仅效率低,还容易出错。于是尝试用InsCode(快马)平台生成通用解析器模块,效果出乎意料地好。 痛点与解决方案 重复劳动问题 以前每爬一个新网站&…...
24小时运行验证:OpenClaw+ollama-QwQ-32B自动化监控脚本稳定性测试
24小时运行验证:OpenClawollama-QwQ-32B自动化监控脚本稳定性测试 1. 项目背景与目标设定 去年冬天的一个深夜,我被手机警报惊醒——某个关键商品的价格突然跌破了我的心理预期。手忙脚乱登录电商平台时,优惠早已结束。这次经历让我意识到&…...
【电赛实战利器】基于STM32F4与协方差修正的全数字锁相放大器设计与实测
1. 为什么你需要一个全数字锁相放大器? 在电子设计竞赛或者精密测量项目中,微弱信号检测总是让人头疼。想象一下,你要从一堆嘈杂的噪音中找出一个微弱的正弦波信号,就像在喧闹的菜市场里听清远处朋友的耳语。传统模拟锁相放大器需…...
Phi-4-Reasoning-Vision开源模型:Phi-4-reasoning-vision-15B双卡推理镜像详解
Phi-4-Reasoning-Vision开源模型:Phi-4-reasoning-vision-15B双卡推理镜像详解 1. 项目概述 Phi-4-Reasoning-Vision是基于微软Phi-4-reasoning-vision-15B多模态大模型开发的高性能推理工具,专为双卡RTX 4090环境优化设计。这个工具严格遵循官方SYSTE…...
ccmusic-database新手教程:Gradio输出组件定制——增强Top5结果可读性技巧
ccmusic-database新手教程:Gradio输出组件定制——增强Top5结果可读性技巧 1. 项目简介与背景 ccmusic-database是一个基于深度学习的音乐流派分类系统,它能够自动识别音频文件所属的音乐流派。这个系统在计算机视觉预训练模型VGG19_BN的基础上进行微调…...
ofa_image-caption生产环境部署:支持批量图片处理与结果导出的企业方案
ofa_image-caption生产环境部署:支持批量图片处理与结果导出的企业方案 1. 项目背景与核心价值 在实际的企业应用中,图像内容理解已经成为许多业务场景的必备能力。无论是电商平台的商品图片描述生成,还是内容平台的海量图片标注࿰…...
Java笔记——JMM
在多线程编程中,共享变量的可见性、操作的原子性以及指令的重排序,常常成为导致程序出现诡异Bug的罪魁祸首。而Java之所以能够成为并发编程的首选语言之一,很大程度上归功于其强大的Java内存模型(Java Memory Model, JMMÿ…...
SRS + FFmpeg WebRTC 循环推流环境搭建
SRS FFmpeg WebRTC 循环推流环境搭建指南 本指南介绍如何使用 Docker Compose 快速搭建一个基于 SRS (Simple Realtime Server) 的流媒体测试环境。 推流协议:RTMP (FFmpeg 模拟推流)拉流协议:WebRTC (低延迟播放)特性:视频循环播放、不保存…...
