定时器的源码介绍与简单实现——多线程编程简单案例[多线程编程篇(5)]
目录
前言
什么是定时器
JAVA标准库中的定时器
而关于sched方法,请看源码:
为什么我们能知道"notify() 唤醒后台线程 TimerThread"?
TimerThread 关键逻辑
第一步:加锁 queue,看有没有任务
第二步:取出最近要执行的任务
第三步:看任务能不能执行
第四步: 判断任务是否是周期性的
第五步: 执行
举个例子,请看代码:
实现一个简单定时器
结尾
前言
在现代软件开发中,多线程编程能力已经成为程序员必须掌握的一项核心技能。随着计算机硬件的不断升级,单核CPU早已无法满足复杂应用的性能需求,多核并行运算已成为主流。而多线程正是实现资源最大化利用、任务高效并行执行的基础手段。
而本博客是笔者多线程编程的第五篇博客! 前面四篇都在下面的URL中,希望能得到大佬们的阅读和指点!!!
多线程编程_callJJ的博客-CSDN博客
本期我们介绍的是定时器,然后翻看并且介绍它的源码,以及如何简单的实现一个定时器,并通过它的简单实现提高我们对于多线程编程的理解,废话不多说,让我们正式开始博客内容!
什么是定时器
在实现定时器之前,我们还是 要先介绍一下什么是定时器
简单来说,定时器(Timer)就是一种允许程序在指定时间后或者每隔一段时间周期性地执行某个任务的机制。它的主要作用是延迟执行或者定时重复执行操作。
在实际开发中,它的用处也很广泛,例如:
界面动画的刷新
游戏中定时生成怪物
服务端每隔一段时间清理缓存
定时发送心跳包检测连接是否存活
定时备份文件系统数据等
举个例子,他就好像是生活中的闹钟,设定达到一个时间以后,就执行某些指定好的代码!
JAVA标准库中的定时器
Java 标准库里最早提供的定时器就是 java.util.Timer
,搭配 java.util.TimerTask
使用。它的核心设计思路非常直接:
1.Timer 本质上是一个单线程的任务调度器,它内部维护一个线程,不断地检查任务队列中下一个要执行的任务时间。它的核心方法为 schedule
它包含两个参数,第一个参数是指定需要执行的任务代码,第二个指令是延长多少 ms
2. TimerTask 则是你要执行的具体任务,需要继承这个抽象类并重写
run()
方法。
我们可以翻看它们的源码略知一二:
public void schedule(TimerTask task, long delay) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");sched(task, System.currentTimeMillis()+delay, 0);}
在主线程中调用 Timer.schedule(TimerTask task, long delay)
,表示安排一个任务在指定延迟后执行。
这一调用并不会直接执行 task.run()
,而是内部调用 sched()
方法,将任务添加到 Timer 的任务队列中。
并且可以看到,schedule方法需要一个 TimerTask对象.
而我们的 TimerTask 是一个继承了 Runnable 接口的 抽象类,所以为了实例化TimerTask,需要重写 run()方法
public abstract class TimerTask implements Runnable
而关于sched方法,请看源码:
private void sched(TimerTask task, long time, long period) {if (time < 0)throw new IllegalArgumentException("Illegal execution time.");// Constrain value of period sufficiently to prevent numeric// overflow while still being effectively infinitely large.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();}}
sched()
方法负责:
先计算出任务的下次执行时间。
然后将
TimerTask
插入到PriorityQueue
(优先队列)中,按时间排序。最后调用
notify()
唤醒后台线程TimerThread
,让它知道有新任务要处理。
为什么我们能知道"notify()
唤醒后台线程 TimerThread
"?
请看 TimerThread 中的代码
private void mainLoop() {while (true) {try {TimerTask task;boolean taskFired;synchronized(queue) {// Wait for queue to become non-emptywhile (queue.isEmpty() && newTasksMayBeScheduled)queue.wait();if (queue.isEmpty())break; // Queue is empty and will forever remain; die// Queue nonempty; look at first evt and do the right thinglong 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) { // Non-repeating, removequeue.removeMin();task.state = TimerTask.EXECUTED;} else { // Repeating task, reschedulequeue.rescheduleMin(task.period<0 ? currentTime - task.period: executionTime + task.period);}}}if (!taskFired) // Task hasn't yet fired; waitqueue.wait(executionTime - currentTime);}if (taskFired) // Task fired; run it, holding no lockstask.run();} catch(InterruptedException e) {}}}
你对比一下 sched()
方法 的代码就会发现,它们有同一个加锁对象—— queue.
如果queue是空的(即当前没有定时任务),并且newTasksMayBeScheduled == true(即允许继续添加新任务),那么当前 TimerThread
就在 queue
上调用 wait()
,挂起自己,等待新的任务到来。
这与前面的 notify()方法遥相呼应
以下是完善流程:
main线程:Timer.schedule(task, delay)-> sched(task, 时间, 周期)-> queue.add(task)-> queue.notify()后台线程(TimerThread):run() -> mainLoop()-> 等待任务-> 到了执行时间-> 调用 task.run()
TimerThread 关键逻辑
那么, TimerThread 关键逻辑 是什么呢?它是怎么一步一步的成功调用 run() 的呢?
第一步:加锁 queue,看有没有任务
synchronized(queue) {while (queue.isEmpty() && newTasksMayBeScheduled)queue.wait();if (queue.isEmpty())break;
}
如果没有任务,就 wait 挂起等待。
如果醒来后发现还是没任务且不允许加了,就 break 退出线程。
第二步:取出最近要执行的任务
task = queue.getMin();
getMin() 取出最早应该执行的那个任务(优先队列小顶堆)。
也就是当前最紧急的任务。
第三步:看任务能不能执行
if (task.state == TimerTask.CANCELLED) {queue.removeMin();continue;
}
如果发现任务被取消了,就从队列移除,继续循环。
然后得到目前的时间,判断能否执行
currentTime = System.currentTimeMillis();executionTime = task.nextExecutionTime;if (taskFired = (executionTime<=currentTime)) {if (task.period == 0) { // Non-repeating, removequeue.removeMin();task.state = TimerTask.EXECUTED;} else { // Repeating task, reschedulequeue.rescheduleMin(task.period<0 ? currentTime - task.period: executionTime + task.period);}
}
设置 标志位 taskFired
executionTime <= currentTime
判断任务的执行时间是否已经到达当前时间。如果到达执行时间,taskFired 被设为 true,表示任务可以执行。如果没有到达执行时间,taskFired 会被设为false,表示任务不能执行,应该等待。
第四步: 判断任务是否是周期性的
如果这个任务是非周期性质的
if (task.period == 0) { // Non-repeating, removequeue.removeMin(); // 从队列中移除任务task.state = TimerTask.EXECUTED; // 将任务状态标记为已执行
}
就把状态设置为 TimerTask.EXECUTED
如果任务是周期性的
else { queue.rescheduleMin(task.period < 0 ? currentTime - task.period : executionTime + task.period);
}
1.task.period != 0 表示任务是周期性任务,即任务需要执行多次。
2.task.period < 0 表示周期性任务的周期是负数。在这种情况下,task.period 表示的是一个相对时间间隔,应该根据当前时间来计算新的执行时间。所以 currentTime - task.period 是计算任务的下次执行时间。
3.否则,executionTime + task.period 表示任务的下次执行时间是基于上一次的执行时间加上一个固定的周期。
以上的这些步骤都是要加锁的,保证原子性
第五步: 执行
如果 taskFired == true , 那么就执行程序
if (taskFired) // Task fired; run it, holding no lockstask.run();
我们小小总结一下吧
在 Java 中,可以通过 Timer 类提供定时器功能。具体来说,定时器的实现主要依赖于调用 schedule() 方法。
schedule() 方法需要传入两个参数:第一个参数是一个实例化的 TimerTask 对象(TimerTask 是一个抽象类,并且实现了 Runnable 接口,需要重写 run() 方法),第二个参数是一个 long delay,表示任务延迟多少毫秒后执行。
在 schedule() 方法内部,只要传入的 delay 参数没有问题(不会抛出异常),方法就会调用内部的 sched() 方法,将任务加入到一个优先队列中。同时,唤醒后台线程 TimerThread 的 mainLoop(),由它不断检查任务是否到达执行时间,一旦到达,就调用任务的 run() 方法,最终实现定时器效果。
举个例子,请看代码:
import java.util.Timer;
import java.util.TimerTask;// 定时器
public class Demo
{public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("2000");}},2000);System.out.println("主线程开始跑");}
}
import java.util.Timer;
import java.util.TimerTask;// 定时器class Mytimertask extends TimerTask
{@Overridepublic void run() {System.out.println("2000");}
}public class Demo
{public static void main(String[] args) {Timer timer = new Timer();Mytimertask mytimertask = new Mytimertask();timer.schedule(mytimertask,2000);System.out.println("主线程开始跑");}
}
我们分别通过实现匿名内部类和实现子类的方法创建了一个 TimerTask 对象,实现了schedule方法
效果如下:
首先打印"主线程开始跑",过了两秒以后打印"2000"
以上就是定时器的源码简单介绍和简单举例
实现一个简单定时器
接下来我们自己动手实现一个简单定时器,通过上面的代码讲解我们也能看出来
我们需要实现一个 TimerTask , 一个 Timer. 然后手动实现schedule()方法 和 mainLoop() 方法
Timer 类
class MyTimerTask
{public Runnable runnable;public long time;public MyTimerTask(Runnable runnable, long delay) {this.runnable = runnable;this.time = System.currentTimeMillis()+delay;}public Runnable getRunnable() {return runnable;}public long getTime() {return time;}
}
为什么手动实现定时器时使用Runnable?
因为我们只需要它的run()方法,不需要别的,TimerTask 所提供的其他更高级的功能我们暂时不弄
TimerTask类
在构造方法中新创建一个线程对象
class MyTimer
{PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>(new Comparator<MyTimerTask>() {@Overridepublic int compare(MyTimerTask o1, MyTimerTask o2) {return Long.compare(o1.time,o2.time);}});Object locker = new Object();public void schedule(Runnable runnable,long delay){synchronized (locker){priorityQueue.offer(new MyTimerTask(runnable,delay));locker.notify();}}
public MyTimer()
{Thread thread = new Thread(() -> {while(true){try {synchronized (locker){while(priorityQueue.isEmpty()){// 如果是空的就阻塞,直到唤醒locker.wait();}MyTimerTask myTimerTask = priorityQueue.peek();long now = System.currentTimeMillis();if (now >= myTimerTask.getTime()) {myTimerTask.getRunnable().run();priorityQueue.poll();} else {locker.wait(myTimerTask.getTime() - now);}}}catch (InterruptedException e){e.printStackTrace();}}});thread.start();
}
}
测试:
public class Demo18 {public static void main(String[] args) {MyTimer myTimer = new MyTimer();myTimer.schedule(new Runnable() {@Overridepublic void run() {System.out.println(3000);}},3000);
// 1️⃣ 创建了一个匿名内部类,实现了 Runnable 接口。
// 2️⃣ 必须重写 run() 方法,否则接口没有具体执行内容。System.out.println("程序开始执行");}
}
结果如下:
结尾
又是写了很久的高质量博客,希望能对人有帮助吧,谢谢大家!!
相关文章:
定时器的源码介绍与简单实现——多线程编程简单案例[多线程编程篇(5)]
目录 前言 什么是定时器 JAVA标准库中的定时器 而关于sched方法,请看源码: 为什么我们能知道"notify() 唤醒后台线程 TimerThread"? TimerThread 关键逻辑 第一步:加锁 queue,看有没有任务 第二步:取出最近要执行的任务 …...
SQL常用数据清洗语句
数据清洗:发现并纠正数据文件里的数据错误和不一致性,让数据达到分析要求的过程。 运用 SQL 进行数据清洗时,可借助多种语句和函数来处理数据中的缺失值、重复值、异常值以及格式错误等问题。 1. 处理缺失值 数据中某些变量的值为空的情况&…...
《Go 语言高并发爬虫开发:淘宝商品 API 实时采集与 ETL 数据处理管道》
在电商数据处理领域,高效获取并处理海量商品数据是企业实现精准运营、市场分析的重要基础。Go 语言凭借其出色的并发性能,成为开发高并发爬虫的理想选择。本文将介绍如何使用 Go 语言进行淘宝商品 API 实时采集,并构建 ETL(Extrac…...
大模型(LLMs)加速篇
当前优化模型最主要技术手段有哪些? 算法层面:蒸馏、量化软件层面:计算图优化、模型编译硬件层面:FP8(NVIDIA H系列GPU开始支持FP8,兼有fp16的稳定性和int8的速度) 推理加速框架有哪一些&#…...
Linux0.11引导启动程序:简略过程
引言 目标:是重写boot文件夹下面的引导文件,加入一些个人信息。语法:由于使用两个语法风格的汇编需要两个汇编器,有些麻烦,直接全都用GNU的 as(gas)进行编译。使用AT&T 语法的汇编语言程序。接下来先拜读同济大学赵…...
【JAVAFX】controller中反射调用@FXML的点击事件失败
场景 当前有一个controller中定义的事件如 FXMLvoid openZhengjieWindow(ActionEvent event) {System.out.println("zhengjie");}通过反射去调用 public void callMethodByString(String methodSuffix) {try {Method method this.getClass().getMethod("open&…...
人工智能数学基础(二):初等数学
在人工智能领域,初等数学知识是构建复杂模型的基石。本文将从函数、数列、排列组合与二项式定理、集合等方面进行讲解,并结合 Python 编程实现相关案例,帮助大家更好地理解和应用这些数学知识。资源绑定附上完整代码供读者参考学习࿰…...
opendds的配置
配置的使用 文档中说明有4种使用配置的方式: 环境变量 命令行参数(将覆盖环境变量中的配置) 配置文件(不会覆盖环境变量或命令行参数中的配置) 用户调用的 API(将覆盖现有配置) 这里对开发…...
mac 基于Docker安装minio
在 macOS 上基于 Docker 安装 MinIO 是一个高效且灵活的方案,尤其适合本地开发或测试环境。以下是详细的安装与配置步骤,结合了最佳实践和常见问题的解决方案: 一、安装 Docker Desktop 下载安装包 访问 Docker 官网,下载适用于 …...
Docker网络架构深度解析与技术实践
目录 第一章 Docker网络架构核心原理 1.1 容器网络模型(CNM)体系 1.2 网络命名空间隔离机制 1.3 虚拟网络设备对(veth) 1.4 网桥驱动模型 第二章 Docker网络模式深度剖析 2.1 Bridge模式(默认模式) …...
如何通过Google Chrome增强网页内容的安全性
在现代互联网环境中,网页安全问题时常困扰着用户。谷歌浏览器作为全球使用最广泛的浏览器之一,提供了多种方式帮助用户增强网页的安全性。以下是一些简单有效的方法,可以帮助用户提高浏览器的安全防护能力。 首先,谷歌浏览器自带了…...
劳动节ppt免费下载,劳动节ppt模板,劳动节课件
劳动节ppt免费下载,劳动节ppt模板,劳动节素材,学生,幼儿园课件:劳动节ppt_模板素材_PPT模板_ppt素材_免抠图片_AiPPTer...
应用在通信网络设备的爱普生晶振SG2016CBN
在数字化浪潮席卷全球的当下,通信网络已成为信息时代的核心基础设施,从 5G 基站的快速部署到数据中心的高效运转,从光纤网络的稳定传输到无线通信的流畅连接,每一个环节都对时钟信号的稳定性和精准性有着极高要求。一个高质量的时…...
STM32 RTC配置
一、什么是RTC? RTC,即实时时钟,是一种能持续运行并保持当前时间信息的电子装置。它常用于在设备断电的情况下依然能保持准确的年、月、日、时、分、秒信息。 与CPU核心时钟不同,RTC通常采用独立的低频晶振(如32.768…...
图像保边滤波之BEEPS滤波算法
目录 1 简介 2 算法原理 3 代码实现 4 演示Demo 4.1 开发环境 4.2 功能介绍 4.3 下载地址 参考 1 简介 BEEPS(Bias Elimination in Edge-Preserving Smoothing) 是一种基于偏微分方程(PDE)的边缘保留平滑滤波算法。它能够…...
使用OpenCV和dlib库进行人脸关键点定位
文章目录 引言一、环境准备二、代码实现解析1. 导入必要的库2. 加载图像和人脸检测器3. 加载关键点预测模型4. 检测并绘制关键点5. 显示结果 三、68个关键点的含义四、常见问题解决五、总结 引言 人脸关键点定位是计算机视觉中的一项基础任务,它在人脸识别、表情分…...
Transformer数学推导——Q27 证明时序注意力(Temporal Attention)在视频模型中的帧间依赖建模
该问题归类到Transformer架构问题集——注意力机制——跨模态与多模态。请参考LLM数学推导——Transformer架构问题集。 在视频理解任务中,捕捉帧与帧之间的时间依赖关系(如动作的连贯性、物体的运动轨迹)是核心挑战。时序注意力(…...
相机-IMU联合标定:相机标定
文章目录 📚简介💡标定方法🚀标定工具kalibr🚀标定数据录制🚀相机标定📚简介 在 VINS(Visual-Inertial Navigation System,视觉惯性导航系统) 中,相机标定 是确保视觉数据准确性和系统鲁棒性的关键步骤,其核心作用可总结为以下方面: 消除镜头畸变,提升特征…...
sevlet API
sevlet API API就是一组类和方法 HttpServlet 这是写servlet代码用到的核心的类,通过继承这个类,并重写其中的方法,让Tomcat去调用这里的逻辑. init:webapp被加载的时候,执行 destroy:webapp被销毁的时候(Tomcat结束)执行,进行一些收尾工作.但是这个方法不保证能够调用到!!…...
python程序设习题答案
第一章 1.在下列领域中,使用 Python 不可能实现的是( C ) A . Web 应用开发 B .科学计算 C .操作系统管理 D .游戏开发 2.Python程序文件的扩展名是( D )。 A.python B.pyt C.pt D.py 3&…...
[计算机科学#4]:二进制如何塑造数字世界(0和1的力量)
【核知坊】:释放青春想象,码动全新视野。 我们希望使用精简的信息传达知识的骨架,启发创造者开启创造之路!!! 内容摘要: 二进制是计算机世界的基石,数学是世界的…...
一种在使用Kaggle并遇上会话中断时强行保存数据的方法
问题:kaggle会话结束后,无法保存训练模型时记录的excel文件 解决方法:使用kaggle时,使用下面脚本可将保存到训练数据excel转为链接形式,从而在kaggle会话终止时也可以下载到该excel文件 import base64 import pandas …...
【人工智能agent】--dify搭建智能体和工作流
【人工智能agent】--docker本地部署dify教程-CSDN博客 上期讲到如何部署dify,然后进入页面: docker服务: 目录 1.基础设置 2.创建聊天助手 3.创建知识库应用 4.创建智能体 5.创建工作流 5.1.文档总结规划 5.2.爬取网页新闻 1.基础设…...
[4282]PHP跨境电商源码-多语言商城源码/支持代理+商家入驻+分销+等等众多功能/带详细安装
源码获取:[4282]PHP跨境电商源码-多语言商城源码/支持代理商家入驻分销等等众多功能/带详细安装云溪资源网_源码下载,小程序源码下载,网站源码下载,游戏源码下载云溪资源网_源码下载,小程序源码下载,网站源码下载,游戏源码下载...
MongoDB的增删改查操作
1.文档创建 首先要插入数据前,要先创建数据库,创建完之后建立集合,然后才能进行增删改查的步骤 切换(新建)数据库: use <db> db是指要创建数据库的名称 新建集合: db.createCollection(…...
TimDbg
晚上随意浏览,发现一个有趣的网站: TimDbg 调试器谎言:堆栈损坏 // TimDbg 2022.11的一篇很有趣,讲如何培养裸眼反汇编的能力,即培训心智模型,模式识别能力。 识别内存中的模式 // TimDbg 我是用edge浏…...
MySQL 表的约束(二)
文章目录 自增长唯一键外键 自增长 auto_increment:当对应的字段,不给值,会自动的被系统触发,系统会从当前字段中已经有的最大值1操作,得到一个新的不同的值。通常和主键搭配使用,作为逻辑主键。 create …...
大数据应用开发和项目实战
Matplotlib的介绍 Matplotlib 是 Python 的绘图库,它能让使用者很轻松地将数据图形化,并且提供多样化的输出格式。 Matplotlib 可以用来绘制各种静态,动态,交互式的图表。比如说散点图、柱状图等等。 Matplotlib Pyplot plot(…...
OpenLayers矢量数据可视化高级技巧(进阶二)
1. 高级样式技术 矢量数据的样式直接影响可视化效果的表达能力和美观度。OpenLayers提供了丰富的样式API,通过组合和创新,可以实现各种复杂的视觉效果。 1.1 动态样式 // 根据属性值动态设置样式 const vectorLayer new ol.layer.Vector({source: ne…...
实用的java技术架构组件汇总
1.后端数据校验 引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency>校验注解 jakarta.validation-api 规范提供如下: size hibern…...
Rmarkdown输出为pdf的方法与问题解决
R 是一种在数据分析与统计计算领域广泛使用的编程语言。其关键优势之一是能够生成高质量的报告和文档,这些报告和文档可以使用 RMarkdown 轻松定制和更新。在本文中,我们将探讨使用 R 从 RMarkdown 文件生成.pdf 文件 1.生成方法 新建Rmarkdown…...
【超详细讲解】什么是序列化和反序列化?
目录 一、什么是序列化(Serialization)? 举个直观的例子 二、什么是反序列化(Deserialization)? 三、为什么需要序列化? 四、常见的序列化格式对比 五、序列化底层是怎么做的?…...
深入浅出JavaScript常见设计模式:从原理到实战(2)
深入浅出JavaScript常见设计模式:从原理到实战(2) 本文是深入浅出JavaScript常见设计模式:从原理到实战(1)的续集 设计模式是一种在特定情境下解决软件设计中常见问题的通用方案或模板。在特定的开发场景中使用特定的设计模式,可以提升代码质…...
MySQL 主从复制
数据的高可用性、读写分离以及数据备份是至关重要的需求。MySQL 作为一款广泛使用的开源关系型数据库,其主从复制功能为解决这些问题提供了有效的方案。本文将详细介绍 MySQL 主从复制的原理、搭建步骤以及实际应用。 一、MySQL 主从复制原理 1.1 基本概念 MySQL…...
小目标检测的集成融合论文阅读
摘要 小目标检测常因图像模糊和分辨率低而受到阻碍,这给小目标的精确检测和定位带来了重大挑战。此外,传统的特征提取方法往往难以捕捉到这些目标的有效表征,因为下采样和卷积操作会导致小目标细节的模糊化。为了解决这些问题,本研究提出了一种基于集成融合的方法,通过利…...
IP SSL证书常见问题:快速实现HTTPS加密
SSL证书作为实现HTTPS加密和身份验证的关键工具,不仅适用于域名,还能直接绑定IP地址,为IP通信提供安全保障。 一、什么是IP SSL证书? IP SSL证书(IP HTTPS证书)是一种专为IP地址设计的SSL/TLS证书…...
Scratch——第20课 辗转相除法/绳子算法
辗转相除法是用于求取最大公约数时需要用到的方法,它还有个名字称为绳子算法,这类题目只要理解辗转相处的原理即可拿下。 一、辗转相除法的基本原理 两个整数的最大公约数不变,当较大数减去较小数后,得到的差值与较小数的最大公…...
MYOJ_1349:(洛谷P3951)[NOIP 2017 提高组] 小凯的疑惑(数学公式套用,两步搞定代码)
提示 本题代码纯属数学的结晶,因此肥肠简单,但需要一定理解。 题目描述 小凯手中有两种面值的金币,两种面值均为正整数且彼此互素。每种金币小凯都有无数个。在不找零的情况下,仅凭这两种金币,有些物品他是无法准确支付…...
如何免费把PPT的页面输出为透明的图片-快速制作图新说汇报内容
0.序 经常有朋友问想把PPT中的内容输出为图片,但是PPT里面的officePlus还得付费才可以。不付费就带水印还不高清,关键是还不透明,如果需要透明就设置纯底色去PS里面抠图(可自动化),或者手动右键挨个输出。…...
操作系统——第四章(文件管理与文件的逻辑结构)
一、文件系统基础 1.文件的属性 文件名:由创建文件的用户决定文件名,主要是为了方便用户找到文件,同一目录下不允许有重名文件标识符:一个系统内的各文件标识符唯一,对用户来说毫无可读性。因此标识符只是操作系统用…...
剑指offer经典题目(七)
目录 动态规划 字符串相关 排序思想相关 链表相关 动态规划 题目1:输入一个长度为n的整型数组array,数组中的一个或连续多个整数组成一个子数组,子数组最小长度为1。求所有子数组的和的最大值。OJ地址 图示如下。 题目解析:…...
[RoarCTF 2019]Easy Calc 详解
[RoarCTF 2019]Easy Calc 1 ajax 是进行前后端交互的 但是我们发现一个waf 就是他提示的"calc.php?num"encodeURIComponent($("#content").val()) ?num 的值必须是数字审计一下 foreach 发现了num的限制但是eval是rce的标志所以我们首选的就是使用命令…...
AI日报 - 2025年04月29日
🌟 今日概览(60秒速览) ▎🤖 AGI突破 | 巨头CEO预测AGI时间线,5年内或达人类认知水平;Yann LeCun强调多模态训练重要性。 关于AGI定义和实现时间的讨论升温,对超越纯文本训练的需求成为共识。 ▎💼 商业动向…...
Kubernetes的错误信息处理
报错信息 E0428 13:18:25.531614 3193818 memcache.go:287] couldn’t get resource list for metrics.k8s.io/v1beta1: the server is currently unable to handle the request 以下是处理该 Kubernetes 指标服务报错的系统化解决方案: 错误诊断流程 # 1. 检查 …...
杰理-安卓通过map获取时间的时候,部分手机切换sbc和aac时候单耳无声音
杰理-安卓通过map获取时间的时候,部分手机切换sbc和aac时候单耳无声音 #if USER_SUPPORT_PROFILE_MAPif(tws_api_get_role()0){ //主机才获取,否则切换sbc 和 aac 的时候影响单耳无声音user_send_cmd_prepare(USER_CTRL_MAP_READ_TIME,0,NULL);} #endif…...
基于 Python 的实现:居民用电量数据分析与可视化
基于 Python 的实现:居民用电量数据分析与可视化 本文将介绍如何利用 Python 技术栈(包括 pymysql、pandas、matplotlib 等库)对居民用电量数据进行分析和可视化,以帮助我们更好地理解用电行为模式。 数据准备 在MySQL数据库中创建数据,,数据库表结构如下: date:记录…...
el-transfer穿梭框数据量过大的解决方案
一:背景 我们这个穿梭框获取的是项目的全量数据,在左边大概有5000条,自己测试了一下5000条数据的效果,发现异常的卡顿,本来打算像el-select一样去解决的(只显示一部分,在搜索的时候去全量搜索&a…...
【3D基础】深入解析OBJ与MTL文件格式:Blender导出模型示例及3D开发应用
引言 在3D模型开发和3D引擎加载过程中,OBJ格式是最基础、最常见的标准之一。即便在今天流行的GLTF、USDZ格式出现后,OBJ依然是建模软件和渲染引擎普遍支持的基本格式。 本文以Blender导出的立方体模型为例,详细讲解OBJ与MTL文件每一部分的含…...
Fiddler+Yakit实现手机流量抓包和小程序抓包
文章目录 一、小程序抓包1、配置Fiddler2、配置Yakit 二、手机流量抓包1、配置Fiddler2、手机连接电脑热点并配置代理服务器3、手机安装证书4、配置Yakit 三、总结 操作工具:Yakit Fiddler 一、小程序抓包 1、配置Fiddler 点击Tools—>Options进入如下配置页面…...
C++实时统计数据均值、方差和标准差
文章目录 1. 算法原理2. 类设计3. 完整代码实现4. 总结 本文采用了一种递推计算方法(Welford 算法)实时更新数据的均值、方差和标准差,其算法原理及实现如下。 1. 算法原理 Welford算法是由B.P.Welford于1962年提出的,用于计…...