Delayed 延时任务

延时任务与定时任务的区别

延时任务,可以理解为定时任务的一种,但是他们是有区别的。
延时任务:将程序代码延时执行,执行完毕,即为结束。
定时任务:周期性执行任务。代码执行完毕后,并不意味着结束,会根据定时的周期时间,继续下次的执行。

如何做定时任务?

之前做过一期的博客:Redis做定时任务
但是是有错误的。我把redis的那个空间变动通知当成定时了!其实他应该算是延时。。。。
那如何做呢?

  • java自带的延时队列
  • redis空间变动通知
  • 可使用定时任务的那种任务调度器做:设置好时间,执行一次,然后关闭。
  • ……
实操

新的项目新的需求,一个物联网的项目:设备爆出警报来后,用户可以将其设置为忽略误报,在多少时间内,不在提示这个错误。
对应到数据上,就是报警数据的[字段:处理状态]修改为已处理,就查不到报警信息了。但是忽略时间一到,状态会自动改回未处理

刚开始,我是想使用Redis来做的,但是想使用Redis,就必须修改配置文件,服务器不在我们手里,修改不了,这个方案被pass掉了。
然后,我使用项目中自带的任务调度器xxl-job(开源的任务调度器,跟Quartz一样的东西),自己计算cron表达式,添加任务,启动任务,然后执行修改方法,关闭任务。但是么,最后。。。被领导臭骂了一顿。说是用延时队列来做。。。

创建操作类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.jetbrains.annotations.NotNull;

import java.util.List;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class DelayedAlarm implements Delayed {
    /**
     * 过期时间。这个时间一定是一个Date类型转成的
     */
    private Long expireTime;
    /**
     * 集合ID
     */
    private List<String> alarmIds;
    /**
     * 是否过期。小于等于0的,表示过期,大于0的,表示未过期,其差值表示还有多少时间过期。
     * 这个我踩了一个大坑。我写的代码为啥就是不会延时执行?
     * 因为这个方法表示还有多少时间过期,一定是过期时间减去当前时间,还剩下多少时间。
     * 而我直接unit.convert(expireTime,TimeUnit.MILLISECONDS)了,
     * 因为只是做了一下时间的转换,每次java调用这个方法判断还有多长时间过期,一直是这个数,所以他就一直延时。。。。
     * @param unit 时间的单位
     * @return 返回排序结果
     */
    @Override
    public long getDelay(@NotNull TimeUnit unit) {
        return unit.convert(expireTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }
    /**
     * 排序方法。用于排序,因为放进来的对象,根据延时时间的大小,不一定是排在后面的,,有可能是排在前面的。
     * @param o 刚加入对象
     * @return 返回排序结果
     */
    @Override
    public int compareTo(@NotNull Delayed o) {
        return (int) (this.getDelay(TimeUnit.MILLISECONDS) - o.getDelay(TimeUnit.MILLISECONDS));
    }
}
业务调用
//省略不重要的导入
public class AlarmServiceImpl {
	//省略其他不重要的注入。
    private final DelayQueue<DelayedAlarm> DELAY_QUEUE = new DelayQueue<>();
   
    @PostConstruct
    public void updateAlarmStatusQueue() {
    	//因为是本地缓存的队列,重启服务会丢失,需要重新查库,重新添加队列
        List<AlarmInfo> list = alarmInfoMapper.getIgnoreAlarmList();
        list.forEach(entity -> {
            long now = System.currentTimeMillis();
            long expireTime = entity.getIgnoreTime() * 1000L + entity.getAlarmStartTime().getTime();
            if (expireTime < now) {
                expireTime = 0;
            }
            DELAY_QUEUE.put(new DelayedAlarm(expireTime, Arrays.asList(entity.getId())));
        });
        //这个地方。等价于 ThreadPoolExecutor executor=Executors.newSingleThreadExecutor()。
        //阿里巴巴代码扫描,总是飘黄,我给改了一下。。
        ThreadPoolExecutor executor = new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(),
                new DefaultThreadFactory("alarm_delaye_queue"));
        executor.execute(new Thread(() -> {
            while (true) {
                try {
                    DelayedAlarm take = DELAY_QUEUE.take();
                    //获取id集合,执行业务逻辑
                    this.updateAlarmStatus(take.getAlarmIds());
                    log.info("延时执行队列成功");
                } catch (Exception e) {
                	//注意:一定要捕获异常,否则出现异常while循环就结束了。
                    log.error("延时执行队列失败", e);
                }
            }
        }));
    }
    /**
     * 忽略误报时间到了,自动修改警报信息状态:已处理-->未处理
     * @param alarmIds 警报信息ID 集合
     * @return
     */
    public void updateAlarmStatus(List<String> alarmIds) {
        List<AlarmInfo> list = new ArrayList<>();
        alarmIds.forEach(id -> {
            //开始更新警报信息状态
            list.add(new AlarmInfo()
                    .setId(id)
                    .setStatus(UN_DISPOSED.getType())
                    .setLastModifyUserId(userUtil.getUserId()));
        });
        //集成了mybaits-plus 插件,我代码给删除了,大家用的时候需要自己写Mapper
        this.updateBatchById(list);
        log.info("延时执行修改报警信息状态成功:{}",JSONObject.toJSONString(alarmIds));
    }
    /**
     * 将当前的忽略误报信息添加延时队列
     * @param ids 警报信息ID 集合
     * @param expireTime 过期时间
     */
    public void handleIgnoreAlarm(List<String> ids,Date expireTime) {
       	//省略不重要的业务。。。。
       	//添加队列
        DELAY_QUEUE.put(new DelayedAlarm(expireTime.getTime(), ids));
    }
}

最后看一下执行结果
在这里插入图片描述

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

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

相关文章

基于单片机仓库温湿度监测报警系统仿真设计

**单片机设计介绍&#xff0c;基于单片机仓库温湿度监测报警系统仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于单片机的仓库温湿度监测报警系统可以被设计成能够实时监测仓库内的温度和湿度&#xff0c;并根据预设…

pytest-base-url插件之配置可选的项目系统URL

前言 ①当我们的自动化代码完成之后&#xff0c;通常期望可以在不同的环境进行测试&#xff0c;此时可以将项目系统的URL单独拿出来&#xff0c;并且可以通过pytest.ini配置文件和支持pytest命令行方式执行。 ② pytest-base-url 是一个简单的pytest插件&#xff0c;它通过命…

关于在3dsmax中制作的模型导入UE后尺寸大小不对的问题

现象 在3dsmax中的基本单位为毫米 在UE中基本单位是厘米 我在3dsmax中创建一个长宽高均为1000mm的方块 然后导入到UE中的世界坐标原点 方块向X轴正方向移动100个单位100cm1000mm&#xff0c;按理来说&#xff0c;新方块的此时应该和旧方块是贴着的&#xff0c;但是现象确是两者…

UE5、CesiumForUnreal实现加载GeoJson绘制盒体(Box)功能(StaticMesh方式)

文章目录 1.实现目标2.实现过程2.1 实现原理2.2 具体代码2.3 应用测试2.3.1 材质2.3.2 蓝图测试3.参考资料1.实现目标 与之前基于StaticMesh创建Polygon和Wall类似,本文通过读取本地GeoJson数据,在UE中以staticMeshComponent的形式绘制出盒体Box,支持Editor和Runtime模式,在…

Idear 中签出git项目分支为灰色

--签出git上的项目 git clone git项目地址 --查看目录 $ dir --查看分支 $ git branch -a --签出分支 $ git checkout origin/v1.0 签出后&#xff0c;使用idear打开项目&#xff0c;项目关联git信息

集成仿真软件 PLEXOS 9.0 授权永久完美

PLEXOS是由能源示例发布的能源市场集成仿真软件。能源市场是一个充满活力和活力的市场&#xff0c;在这个大市场中有各种各样的数据和信息模型。观看每日市场发展和大量数据可能会使您感到困惑&#xff0c;并难以进行未来的投资和分析。使用集成和全面的方法是应对这一挑战的唯…

vivado产生报告阅读分析14-时序报告10

Vivado IDE 中的例外报告 “ Report Exceptions ”对话框 在 AMD Vivado ™ IDE 中 &#xff0c; 选择“ Reports ” → “ Timing ” → “ Report Exceptions ” &#xff08; 报告 > 时序 > 例外报告 &#xff09; 即可打开“Report Exceptions ”对话框。 从“…

香港科技大学广州|机器人与自主系统学域博士招生宣讲会—同济大学专场!!!(暨全额奖学金政策)

在机器人和自主系统领域实现全球卓越—机器人与自主系统学域 硬核科研实验室&#xff0c;浓厚创新产学研氛围&#xff01; 教授亲临现场&#xff0c;面对面答疑解惑助攻申请&#xff01; 一经录取&#xff0c;享全额奖学金1.5万/月&#xff01; &#x1f559;时间&#xff1a;…

前端环境变量释义

视频教程 彻底搞懂前端环境变量使用和原理&#xff0c;超清楚_哔哩哔哩_bilibili 添加命令行参数 --modexxxxx 新建.env.xxxx文件,其中.env文件会在所有环境下生效 以VITE_开头&#xff0c;字符串无需加双引号 使用import.meta.env.VITE_xxxxx进行调用

【STM32】TF卡FTA32文件系统

一、SD卡介绍 1.SD简介 本质&#xff1a;NandFlash控制芯片 2.SD卡存储容量等级 3.FAT文件系统的使用 4.SD卡速度等级 5.SD卡驱动方式 1.SDIO&&SD 1&#xff09;SDIO接口通信线&#xff1a;CLK/CMD/DAT0-3&#xff08;数据传输线4根&#xff09; 2&#xff09;SPI接口…

Linux:安装IDEA开发工具

1、下载 下载地址&#xff1a;https://www.jetbrains.com/idea/download/?sectionlinux 2、上传及解压 将安装包上传到虚拟机&#xff0c;我建的目录是/opt/idea 解压&#xff1a;tar -zxvf ideaIU-2023.2.5.tar.gz 3、启动 启动IDEA需要登陆桌面系统&#xff0c;不能在…

78基于matlab的BiLSTM分类算法,输出迭代曲线,测试集和训练集分类结果和混淆矩阵

基于matlab的BiLSTM分类算法&#xff0c;输出迭代曲线&#xff0c;测试集和训练集分类结果和混淆矩阵&#xff0c;程序有详细注释&#xff0c;数据可更换自己的&#xff0c;程序已调通&#xff0c;可直接运行。

Java实现象棋算法

象棋算法包括搜索算法、评估函数和剪枝算法。以下是一个简单的实现&#xff1a; 搜索算法&#xff1a;使用极大极小值算法&#xff0c;即每个玩家都会做出最好的选择&#xff0c;考虑到对方也会做出最好的选择&#xff0c;所以需要搜索多层。 public int search(int depth, i…

C++ 使用c++类模板实现动态数组-可实现自定义数据类型存储

.hpp文件 #include <iostream> #include <cstdlib> #include <cstring> using namespace std; template <class T> class arraylist { private:T* data ;//数组地址int size;//长度int count;//容量public:arraylist();~arraylist();void add(T t);T&…

C++——stack和queue

目录 stack的介绍和使用 stack的使用 queue的介绍和使用 queue的使用 容器适配器 deque的介绍 deque的缺陷 priority_queue的介绍和使用 priority_queue的使用 仿函数 反向迭代器 stack的介绍和使用 在原来的数据结构中已经介绍过什么是栈了&#xff0c;再来回顾一下…

接口自动化测试:pytest基础讲解

为什么要做接⼝测试&#xff1f; 只靠前端测试很难确保很⾼的覆盖率。接⼝测试&#xff0c;可以模拟出各种类型的⼊参&#xff0c;包括⼀些在前端模拟不出来的⼊参&#xff0c;还能根据接⼝⽂档的定义&#xff0c;设计出相对完善的⼊参值&#xff0c;在接⼝层保证质量&#xf…

如何搭建测试环境?一文解决你所有疑惑!

什么是测试环境 测试环境&#xff0c;指为了完成软件测试工作所必需的计算机硬件、软件、网络设备、历史数据的总称&#xff0c;简而言之&#xff0c;测试环境硬件软件网络数据准备测试工具。 硬件&#xff1a;指测试必需的服务器、客户端、网络连接等辅助设备。 软件&#…

【算法之路】高精度算法(实现加减乘除)

目录 一、高精度概念 二、高精度算法的实现 1、高精度加法&#xff08;大整数相加&#xff09; 2、高精度减法&#xff08;大整数减法&#xff09; 3、高精度乘法&#xff08;大整数*小整数&#xff09; 4、高精度除法&#xff08;大整数/小整数&#xff09; 一、高精度概…

做接口自动化遇到的20个难点,记录下我是如何解决的!

我是一名接口自动化测试工程师&#xff0c;在公司中负责接口自动化测试的设计和执行。在公司中&#xff0c;接口自动化测试非常重要&#xff0c;因为公司的业务场景非常复杂&#xff0c;需要保证接口的质量。在这篇文章中&#xff0c;我将分享我在公司中接口自动化测试遇到的20…

代码随想录二刷 | 链表 |两两交换链表中的节点

代码随想录二刷 &#xff5c; 链表 &#xff5c;两两交换链表中的节点 题目描述解题思路 & 代码实现 题目描述 24.两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本…