Java多线程技术四——定时器(备份)

 1 定时器的使用

        在JDK库中Timer类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务,Timer类的方法列表如下:

        Timer类的主要作用就是设置计划任务,封装任务的类却是TimerTask,该类的结构如下图

 

        因为TimerTask是一个抽象类,所以计划执行的代码要放入Timer-Task的子类中。

2 schedule(TimerTask task,Date time)方法

        该方法的作用是在指定的日期执行一个某个任务。

2.1 执行任务的时间晚于当前时间

public class MyTask extends TimerTask {@Overridepublic void run(){System.out.println("任务执行了,时间 = " + Utils.data(System.currentTimeMillis()));}
}

public class Run1 {public static void main(String[] args) throws InterruptedException {long nowTime = System.currentTimeMillis();System.out.println("当前时间 = " + Utils.data(nowTime));long scheduleTime = (nowTime + 10000);System.out.println("计划时间 = " + Utils.data(scheduleTime));MyTask task = new MyTask();Timer timer = new Timer();Thread.sleep(1000);timer.schedule(task,new Date(scheduleTime));Thread.sleep(Integer.MAX_VALUE);}
}

        10秒之后任务成功执行。任务虽然执行完成了,但进程还未销毁,呈红色状态,说明内部还有非守护线程正在执行。为什么会出现这个情况,请看下面的介绍。

2.2 线程TimerThread不销毁的原因

        进程不销毁的原因是在创建Timer类时启动了1个新的非守护线程,JDK源码如下:

 public Timer() {this("Timer-" + serialNumber());}

         此构造方法调用的是如下的构造方法。

 private final TimerThread thread = new TimerThread(queue);public Timer(String name, boolean isDaemon) {var threadReaper = new ThreadReaper(queue, thread);this.cleanup = CleanerFactory.cleaner().register(this, threadReaper);thread.setName(name);thread.setDaemon(isDaemon);thread.start();}

        查看构造方法可以,创建1个Timer类时,在内部就启动了1个新的线程,用新启动的这个线程去执行计划任务,TimerThread是线程类,源代码如下:

class TimerThread extends Thread {}

        这个新启动的线程并不是守护线程,而且一直在运行。一直在运行的原因是新线程内部有一个死循环,TimerThread.java类中的mainLoop()方法的代码如下:

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) {}}}

        mainLoop()方法内部使用while(true)死循环一直执行计划任务,并不退出while(true)死循环,根据源代码的执行流程,除非是满足if(queue.isEmpty())条件,才执行break,退出while(true)死循环,退出逻辑的核心源码是:

                    while (queue.isEmpty() && newTasksMayBeScheduled)queue.wait();if (queue.isEmpty())break;

        上面代码的逻辑如下:

        1、使用while循环对queue.isEmpty() && new TashMaryBeScheduled条件进行判断。

        2、当&&两边运算结果都为true时,执行wait()方法使当前线程被暂停运行,等待被唤醒。

        3、唤醒的时机是还行了public void schedule(TimerTask task,Date time)方法,说明要执行新的任务了。

        4、唤醒后while继续判断queue isEmpty()&& new TashMaryBeScheduled条件,如果有新的任务被安排,则queue.isEmpty()结果为false,&&最终结果是false,会继续执行下面的if语句。

        5、if(queue.isEmpty)结果为true,说明队列为空,那么执行break语句退出while(true)死循环。

3 使用public void cancel方法实现TimerThread线程销毁

        Timer类中public void cancel方法 的作用是终止此计时器,丢弃当前所有已安排的任务。这不会干扰当前正在执行的任务(如果存在)。一旦终止了计时器,那么它的执行线程也会终止,并且无法根据它安排更多的任务。注意,在此计时器调用的计时器任务的run()方法内调用此方法,就可以确保正在执行的任务是此计时器所执行的最后一个任务。虽然可以重复调用此方法,但是第二次和后续调用无效。

public class MyTask extends TimerTask {@Overridepublic void run() {System.out.println("任务执行了,时间是:" + System.currentTimeMillis());}
}
public class Run1 {public static void main(String[] args) throws InterruptedException {long nowTime = System.currentTimeMillis();System.out.println("当前时间是:" + nowTime);long sch = (nowTime + 15000);System.out.println("计划执行时间是:" + sch);MyTask task = new MyTask();Timer timer = new Timer();timer.schedule(task,new Date(sch));Thread.sleep(18000);timer.cancel();Thread.sleep(Integer.MAX_VALUE);}
}

        

        运行了18秒后,Timer-0的线程TimerThread被销毁了,但是进程还是呈红色状态,这是因为main线程一直在执行Thread.sleep(Interger.MAX_VALUE) 代码。

4 执行任务的时间早于当前时间(立即运行)的效果

public class MyTask extends TimerTask {@Overridepublic void run() {System.out.println("任务执行了,时间是:" + Utils.data(System.currentTimeMillis()));}
}
public class Run1 {public static void main(String[] args) {long nowTime = System.currentTimeMillis();System.out.println("当前时间是:" + Utils.data(nowTime));long sch = (nowTime - 5000);System.out.println("计划执行时间是:" + Utils.data(sch));MyTask task = new MyTask();Timer timer = new Timer();timer.schedule(task,new Date(sch));}
}

5 在定时器中执行多个TimerTask任务

        可以在定时器中执行多个TimerTask任务。

public class MyTask extends TimerTask {@Overridepublic void run() {System.out.println("任务执行了,时间是:" + Utils.data(System.currentTimeMillis()));}
}

public class Run1 {public static void main(String[] args) {long nowTime = System.currentTimeMillis();System.out.println("当前时间是:" + Utils.data(nowTime));long sch1 = (nowTime + 5000);long sch2 = (nowTime + 8000);System.out.println("计划执行时间1是: "+ Utils.data(sch1));System.out.println("计划执行时间2是: "+ Utils.data(sch2));MyTask task1 = new MyTask();MyTask task2 = new MyTask();Timer timer = new Timer();timer.schedule(task1,new Date(sch1));timer.schedule(task2,new Date(sch2));}
}

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

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

相关文章

Michael Jordan:大模型在两个方向仍需“努力”丨智源专访

导读 回望过去的这一年&#xff0c;大模型的出现永远改变了当前的技术产业格局&#xff0c;却鲜少有人说起大模型真正的致命缺点。 “如果你问 ChatGPT&#xff0c;乌干达总统是谁&#xff1f;它会给出一个答案。但你问它对刚才说的话有多确定&#xff1f;它表示无法回答。”提…

手把手教你在飞书中搭建机器人

前言 大家好&#xff0c;我是潇潇雨声。飞书是一款在国内广受欢迎的企业内部管理和协同工具&#xff0c;同时也可以作为一个强大的个人知识管理工具。在本文中&#xff0c;我将帮助你迅速创建一个飞书对话机器人&#xff0c;并嵌入 chatGPT 的功能。这个机器人可以直接回答你的…

Ansible3

Templates 模块 Jinja模版架构&#xff0c;通过这个模版可以实现&#xff0c;往配置文件(模版文件)传参(python转义)把占位符参数传到配置文件中去 Jinja就是生产一个目标文本文件&#xff0c;然后传递变量需要配置文件&#xff08;web开发&#xff09; 创建文件 123 345 678…

【数据结构一】初始Java集合框架(前置知识)

Java中的数据结构 Java语言在设计之初有一个非常重要的理念便是&#xff1a;write once&#xff0c;run anywhere&#xff01;所以Java中的数据结构是已经被设计者封装好的了&#xff0c;我们只需要实例化出想使用的对象&#xff0c;便可以操作相应的数据结构了&#xff0c;本篇…

最小二乘法简介

最小二乘法简介 1、背景描述2、最小二乘法2.1、最小二乘准则2.2、最小二乘法 3、最小二乘法与线性回归3.1、最小二乘法与线性回归3.2、最小二乘法与最大似然估计 4、正态分布&#xff08;高斯分布&#xff09; 1、背景描述 在工程应用中&#xff0c;我们通常会用一组观测数据去…

Linux多线程:POSIX信号量,基于信号量的环形队列实现生产者消费者模型

目录 一、POSIX信号量1.1 初始化信号量1.2 销毁信号量1.3 等待信号量1.4 发布信号量1.5 基于环形队列的生产消费模型(用信号量控制生产者和消费者之间的同步互斥关系)1.5.1 makefile1.5.2 RingQueue.hpp1.5.3 Sem.hpp1.5.4 Task.hpp1.5.5 main.cc 二、信号量控制的环形队列原理…

Angular 11到升级到 Angular 16

日新月异&#xff0c;与时俱进… 随着Angular版本不断更新&#xff0c;再看所开发的项目版本仍然是Angular 11&#xff0c;于是准备升级 截止发博日最版本是 v17.1.0&#xff0c;考虑到稳定性因素决定升级到v16版本 一&#xff1a;查看 升级指南 二&#xff1a;按照指南&…

【Element】el-table 使用 el-table-infinite-scroll 插件实现滚动加载

虽然 el 官方提供了 Infinite Scroll 无限滚动 组件 但是却不支持 el-table 组件&#xff0c;这就很难受了&#xff0c;还好已经有大佬写好了插件&#xff0c;并且支持 element-plus/infinite-scroll 组件的所有选项。 el-table-infinite-scroll el-table-infinite-scroll 看…

「微服务模式」七种微服务反模式

什么是微服务 流行语经常为进化的概念提供背景&#xff0c;并且需要一个良好的“标签”来促进对话。微服务是一个新的“标签”&#xff0c;它定义了我个人一直在发现和使用的领域。文章和会议描述了一些事情&#xff0c;我慢慢意识到&#xff0c;过去几年我一直在发展自己的个人…

HarmonyOS的装饰器之BuilderParam 理解

BuilderParam 装饰器 使用时间&#xff1a;当定义了一个子组件&#xff0c;并且子组件的build()中有一个布局在不同的父组件&#xff0c;实现效果不一样的时候&#xff0c;可以在子组件中用这个BuilderParam装饰器&#xff0c; 在父组件用Builder 装饰器进行实现&#xff0c;然…

Netty-2-数据编解码

解析编解码支持的原理 以编码为例&#xff0c;要将对象序列化成字节流&#xff0c;你可以使用MessageToByteEncoder或MessageToMessageEncoder类。 这两个类都继承自ChannelOutboundHandlerAdapter适配器类&#xff0c;用于进行数据的转换。 其中&#xff0c;对于MessageToMe…

【MybatisPlus快速入门】(3)SpringBoot整合MybatisPlus 之 Lombok插件安装及MybatisPlus分页代码示例

目录 1.Lombok1.1 步骤1:添加lombok依赖 2.2 步骤2:安装Lombok的插件1.3 步骤3:模型类上添加注解2 分页功能2.1 步骤1:调用方法传入参数获取返回值2.2步骤2:设置分页拦截器2.3 步骤3:运行测试程序 之前我们已学习MyBatisPlus在代码示例与MyBatisPlus的简介&#xff0c;在这一节…