教育网站前置审批系统/鹤壁网络推广哪家好

教育网站前置审批系统,鹤壁网络推广哪家好,注册安全工程师考试结果查询,泰州做兼职的网站前言 Java 提供的java.util.Timer类可以用来执行延时任务,任务可以只执行一次,也可以周期性的按照固定的速率或延时来执行。 实现一个延时任务调度器,核心有两点: 如何存储延时任务如何调度执行延时任务 源码分析 TimerTask …

前言

Java 提供的java.util.Timer类可以用来执行延时任务,任务可以只执行一次,也可以周期性的按照固定的速率或延时来执行。

实现一个延时任务调度器,核心有两点:

  1. 如何存储延时任务
  2. 如何调度执行延时任务

源码分析

TimerTask

延时任务的核心属性有两个:

  1. 任务的逻辑(任务要干什么)
  2. 任务计划执行时间

Java 把延时任务封装成java.util.TimerTask类,实现自 Runnable,可以被线程执行。

public abstract class TimerTask implements Runnable {int state = VIRGIN;// 默认值,表示任务还没被安排调度执行static final int VIRGIN = 0;// 任务入队,等待调度static final int SCHEDULED   = 1;// 任务执行中static final int EXECUTED    = 2;// 任务取消static final int CANCELLED   = 3;long nextExecutionTime;long period = 0;
}

属性说明:

  • state 任务的状态,例如 执行中、已取消等
  • nextExecutionTime 任务下次执行的时间
  • period 任务重复执行的时间周期,正值表示固定速率执行,负值表示固定延时执行,0表示非重复任务

Timer

Java 把延时任务调度器封装成java.util.Timer

public class Timer {private final TaskQueue queue = new TaskQueue();private final TimerThread thread = new TimerThread(queue);
}

属性说明:

  • queue 任务优先级队列,基于最小堆实现,队头永远是最早要执行的任务
  • thread 任务调度执行线程,单线程执行

提交延时任务,最终会调用sched(),给 task 赋值任务的执行时间,状态等信息,然后加入到队列,如果队头就是自己,那么就要唤醒线程轮询队列调度执行。

private void sched(TimerTask task, long time, long period) {if (time < 0)throw new IllegalArgumentException("Illegal execution time.");if (Math.abs(period) > (Long.MAX_VALUE >> 1))period >>= 1;synchronized(queue) {if (!thread.newTasksMayBeScheduled)throw new IllegalStateException("Timer already cancelled.");// 抢锁synchronized(task.lock) {if (task.state != TimerTask.VIRGIN)throw new IllegalStateException("Task already scheduled or cancelled");// 设置任务的执行时间,重复执行的时间周期,状态改为已调度task.nextExecutionTime = time;task.period = period;task.state = TimerTask.SCHEDULED;}// 入队queue.add(task);// 如果队头是自己,唤醒线程调度执行if (queue.getMin() == task)queue.notify();}
}

TimerThread

执行延时任务的线程被封装成java.util.TimerThread类,继承自 Thread,内部持有任务队列的引用。

class TimerThread extends Thread {private TaskQueue queue;
}

线程执行会调用mainLoop(),不断轮询任务队列,如果队列是空的,线程就 wait,等待外部提交延时任务将自己唤醒。如果队列非空,就判断队头节点的执行时间是否已到,时间到了就立即执行任务,否则就 wait 指定时间。如果任务是一次性的,就把它从堆中移除;如果任务是重复执行的,就再重新添加到堆中。

private void mainLoop() {while (true) {try {TimerTask task;boolean taskFired;synchronized(queue) {// 队列空就waitwhile (queue.isEmpty() && newTasksMayBeScheduled)queue.wait();if (queue.isEmpty())break; // Queue is empty and will forever remain; die// 获取队头节点long currentTime, executionTime;task = queue.getMin();synchronized(task.lock) {if (task.state == TimerTask.CANCELLED) {queue.removeMin();continue;  // No action required, poll queue again}currentTime = System.currentTimeMillis();executionTime = task.nextExecutionTime;if (taskFired = (executionTime<=currentTime)) {if (task.period == 0) {// 一次性任务,删除掉queue.removeMin();task.state = TimerTask.EXECUTED;} else {// 重复执行的任务,再提交一次queue.rescheduleMin(task.period<0 ? currentTime   - task.period: executionTime + task.period);}}}if (!taskFired)// 任务执行时间还没到,继续waitqueue.wait(executionTime - currentTime);}if (taskFired)// 任务执行时间到了,就执行task.run();} catch(InterruptedException e) {}}
}

注意,period>0 代表任务以 **固定速率 **执行,period<0 代表任务以 **固定延时 **执行。

什么意思呢?固定速率模式下,任务的下次执行时间会以上次任务的计划执行时间开始计算,上次任务执行的耗时尽量不影响下次任务的执行时间。固定延时模式下,任务的下次执行时间会以上次任务的实际执行时间开始计算,也就是说上次任务的执行耗时会影响下次任务的执行时间。

TaskQueue

存储任务的队列被封装成java.util.TaskQueue类,它是一个基于最小堆实现的优先队列,堆中的元素会按照任务的计划执行时间升序排列,队头永远是最早要执行的任务,这样获取要执行的任务时间复杂度是O(1),但是任务的插入删除,时间复杂度是O(logn)。

因为堆是一棵完全二叉树,数据规模为n的情况下,二叉树的高度是 logn。堆节点的插入和删除,需要不断和父节点或子节点比较并交换,交换的次数最多是树的高度,所以时间复杂度最坏是O(logn)。

class TaskQueue {private TimerTask[] queue = new TimerTask[128];private int size = 0;
}

属性说明:

  • queue 任务队列,最小堆结构,用数组存储
  • size 任务数

提交任务会调用add(),一个基本的往堆中添加节点的操作。先判断数组是否要扩容,然后添加到队尾,最后节点上滤,调整堆结构。

void add(TimerTask task) {// 扩容if (size + 1 == queue.length)queue = Arrays.copyOf(queue, 2*queue.length);// 先入到队尾queue[++size] = task;// 上滤操作,调整堆结构fixUp(size);
}

堆节点上滤过程很简单,不断与父节点比较任务的执行时间,如果父节点任务晚于自己执行,就要和父节点交换位置。

/**
* 元素上滤,调整堆结构
* 不断与父节点比较,如果父节点比自己大,就要和父节点交换
*/
private void fixUp(int k) {// 是否有父节点while (k > 1) {// j = 父节点下标int j = k >> 1;// 如果父节点的执行时间大于自己,就要交换,直到自己排到正确的位置if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)break;TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;k = j;}
}

如果是一次性任务,执行前要把它从堆中移除,调用removeMin()。先把队尾节点赋值给队头节点,然后将队头节点下滤操作。

void removeMin() {queue[1] = queue[size];queue[size--] = null;fixDown(1);
}

堆节点下滤也很简单,就是不断和自己的左右子节点比较,如果子节点比自己小,就要交换,直到自己被交换到正确的位置。

private void fixDown(int k) {int j;// 是否有子节点 j=左子节点,j+1=右子节点while ((j = k << 1) <= size && j > 0) {if (j < size &&queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)j++; // j indexes smallest kidif (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)break;TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;k = j;}
}

尾巴

延时任务调度执行器的核心有两点,分别是如何存储任务以及如何调度执行任务。Java Timer 基于最小堆的数据结构来存储延时任务,根节点永远是最早执行的任务,获取任务的时间复杂度是O(1),但是任务的插入和删除需要O(logn)复杂度,这在需要维护大量延时任务时,会有性能问题,可以考虑采用时间轮算法。

另外,每个 Java Timer 对象都会开启一个线程来调度执行提交的所有任务,因为是单线程执行,所以一旦有耗时的任务,队列中的其它任务都会受到影响,这点尤其要注意。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

系统思考—啤酒游戏经营决策沙盘模拟

再次感谢文华学院的邀请&#xff0c;为经纬集团管理层带来 《啤酒游戏经营决策沙盘》&#xff01; 很多朋友问&#xff1a;“最近是不是啤酒游戏上的少了&#xff1f;” 其实&#xff0c;真正的关键不是游戏本身&#xff0c;而是——如何让大家真正看见复杂系统中的隐性结构。 …

排序算法实现:插入排序与希尔排序

目录 一、引言 二、代码整体结构 三、宏定义与头文件 四、插入排序函数&#xff08;Insertsort&#xff09; 函数作用 代码要点分析 五、希尔排序函数&#xff08;ShellSort&#xff09; 函数作用 代码要点分析 六、打印数组函数&#xff08;PrintSort&#x…

2025-03-20 学习记录--C/C++-C 库函数 - toupper()、tolower()、 isspace()

合抱之木&#xff0c;生于毫末&#xff1b;九层之台&#xff0c;起于累土&#xff1b;千里之行&#xff0c;始于足下。&#x1f4aa;&#x1f3fb; 一、C 库函数 - toupper() ⭐️ C 标准库 - <ctype.h> C 标准库的 ctype.h 头文件提供了一些函数&#xff0c;可用于测试和…

易语言模拟真人鼠标轨迹算法

一.简介 鼠标轨迹算法是一种模拟人类鼠标操作的程序&#xff0c;它能够模拟出自然而真实的鼠标移动路径。 鼠标轨迹算法的底层实现采用C/C语言&#xff0c;原因在于C/C提供了高性能的执行能力和直接访问操作系统底层资源的能力。 鼠标轨迹算法具有以下优势&#xff1a; 模拟…

sparksql的Transformation与 Action操作

Transformation操作 与RDD类似的操作 map、filter、flatMap、mapPartitions、sample、 randomSplit、 limit、 distinct、dropDuplicates、describe&#xff0c;而以上这些都是企业中比较常用的&#xff0c;这里在一个文件中统一论述 val df1 spark.read.json("src/m…

微软Data Formulator:用AI重塑数据可视化的未来

在数据驱动的时代,如何快速将复杂数据转化为直观的图表是每个分析师面临的挑战。微软研究院推出的开源工具 Data Formulator,通过结合AI与交互式界面,重新定义了数据可视化的工作流。本文将深入解析这一工具的核心功能、安装方法及使用技巧,助你轻松驾驭数据之美。 一、Dat…

本地部署deepseek-r1建立向量知识库和知识库检索实践【代码】

目录 一、本地部署DS 二、建立本地知识库 1.安装python和必要的库 2.设置主目录工作区 3.编写文档解析脚本 4.构建向量数据库 三、基于DS,使用本地知识库检索 本地部署DS,其实非常简单,我写了一篇操作记录,我终于本地部署了DeepSeek-R1(图文全过程)-CSDN博客 安装…

String、StringBuffer、StringBuiler的区别

可变性 String是不可变的&#xff0c;这是因为String内部用于存储数据的char[]数组用了final关键字修饰&#xff0c;而且是private的&#xff0c;并且没有对外提供修改数组的方法。 StringBuffer和StringBuilder是可变的&#xff0c;它们内部的char数组没有用final关键字修饰。…

Certd自动化申请和部署SSL证书并配置https

服务器使用的华为云&#xff0c;之前SSL证书通过配置Cloudflare的DNS实现的&#xff0c;最近华为云备案提示需修改解析至境内华为云IP&#xff0c;若解析境外IP&#xff0c;域名无需备案&#xff0c;需注销或取消接入备案信息&#xff0c;改为使用Certd自搭建证书管理工具&…

git tag以及git

git tag 以及git 一、先说收获吧 1. git bash 在windows上 类似于linux的bash提供的shell命令行窗口&#xff0c;可以执行很多linux命令&#xff0c;cd pwd ls vim cat touch mkdir&#xff0c;还可以用正则匹配查看标签。相当于在windows上装了一个小的linux。git init myproj…

ESP8266通过AT指令配置双向透传

一、固件烧录 IO0接地后上电&#xff0c;进入烧录模式&#xff0c;烧录完成后去掉即可 二、参数配置 1、服务器端 ATCWMODE_DEF2 ATCWSAP_DEF"ESP8266","12345678",5,3 ATSAVETRANSLINK1,"192.168.4.2",9090,"UDP",8080 2、客户端…

【3D模型】【游戏开发】【Blender】Blender模型分享-狮头木雕附导入方法

导入方法&#xff1a; [Blender] 如何导入包含纹理的 .blend 模型文件 在 3D 建模和渲染工作中&#xff0c;Blender 是一款功能强大的免费开源软件。很多时候&#xff0c;我们需要导入 .blend 后缀的模型文件&#xff0c;同时确保纹理&#xff08;textures&#xff09;文件夹…

C# | 超简单CSV表格读写操作(轻松将数据保存到CSV,并支持读取还原)

C# | 超简单CSV表格读写操作&#xff08;轻松将数据保存到CSV&#xff0c;并支持读取还原&#xff09; 文章目录 C# | 超简单CSV表格读写操作&#xff08;轻松将数据保存到CSV&#xff0c;并支持读取还原&#xff09;一、上位机开发中的CSV应用背景二、CSV读写实战教学1. 基本对…

Unity导出WebGL,无法显示中文

问题&#xff1a;中文无法显示 默认字体无法显示中文 在编辑器中设置了中文和英文的按钮&#xff0c;中文按钮无法显示 导出后无法显示中文 解决办法&#xff1a; 自己添加字体&#xff0c;导入项目&#xff0c;并引用 示例 下载一个字体文件&#xff0c;这里使用的阿里…

阅读《Vue.js设计与实现》 -- 02

接上一篇文章&#xff1a;阅读《Vue.js设计与实现》 – 01 文章目录 第二章提升用户的开发体验tips 控制框架代码的体积Tree-Shaking副作用 框架应该输出怎样的构建产物&#xff1f;注意这两个文件有什么区别&#xff1f; 特性开关如何实现&#xff1f; 处理错误TS支持 第二章 …

Mac:Ant 下载+安装+环境配置(详细讲解)

&#x1f4cc; 下载 Ant 下载地址&#xff1a;https://ant.apache.org/bindownload.cgi &#x1f4cc; 无需安装 Apache官网下载 Ant 压缩包&#xff0c;无需安装&#xff0c;下载解压后放到自己指定目录下即可。 按我自己的习惯&#xff0c;我会在用户 jane 目录下新建了个…

蓝桥杯国赛子串2023动态规划,暴力

#include <bits/stdc.h> using namespace std; // string ss; #define int long long string s; //该方法通过动态规划&#xff0c;找到2023字串&#xff0c;而2023等于202加3&#xff0c;202等于202&#xff0c;20等于20&#xff1b; int f2() {int dp[4]{0};//dp[0]代表…

Vue3项目开发:状态管理实践指南

# Vue3项目开发&#xff1a;状态管理实践指南 一、引言 背景介绍 在Vue项目中&#xff0c;状态管理是一个非常重要的话题。合理的状态管理能够帮助我们更好地组织和管理数据&#xff0c;提升项目的可维护性和可扩展性。本文将深入探讨Vue3项目中状态管理的最佳实践&#xff0c;…

网络安全漏洞与修复 网络安全软件漏洞

文章目录 一、软件漏洞的概念 1、信息安全漏洞简述2、软件漏洞3、软件漏洞概念4、软件漏洞的成因分析 二、软件漏洞标准化管理 1、软件漏洞分类2、软件漏洞分级3、安全漏洞管理规范 一、软件漏洞的概念 1、信息安全漏洞简述 信息安全漏洞是信息安风险的主要根源之一&…

SpringBoot项目controller层接收对应格式请求的相关RequestMapping配置

目录 &#xff08;1&#xff09; &#xff08;2&#xff09; &#xff08;3&#xff09; 注&#xff1a;此情况注意和&#xff08;4&#xff09;中情况进行区分 &#xff08;4&#xff09; 在几个springboot项目开发后&#xff0c;我总结了以下的一些常见的接收对应请求的…