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/297843.html

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

相关文章

Http---查看HTTP协议的通信过程

1. 谷歌浏览器开发者工具的使用 首先需要安装Google Chrome浏览器&#xff0c;然后Windows和Linux平台按F12调出开发者工具, mac OS选择 视图 -> 开发者 -> 开发者工具或者直接使用 altcommandi 这个快捷键&#xff0c;还有一个多平台通用的操作就是在网页右击选择检查。…

uni-app 工程目录结构介绍

锋哥原创的uni-app视频教程&#xff1a; 2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中..._哔哩哔哩_bilibili2023版uniapp从入门到上天视频教程(Java后端无废话版)&#xff0c;火爆更新中...共计23条视频&#xff0c;包括&#xff1a;第1讲 uni…

Vue2从源码角度来回答一些常见的问题

1.请说一下Vue2响应式数据的理解&#xff08;先知道基本的问题在哪里&#xff0c;源码的角度来回答&#xff0c;用的时候会有哪些问题&#xff09; 可以监控一个数据的修改和获取操作。针对对象格式会给每个对象的属性进行劫持 Object.defineProperty 源码层面 initData ->…

嵌入式开发——PWM高级定时器

学习目标 加强掌握PWM开发流程理解定时器与通道的关系掌握多通道配置策略掌握互补PWM配置策略掌握定时器查询方式掌握代码抽取优化策略掌握PWM调试方式学习内容 需求 点亮8个灯,采用pwm的方式。 定时器 通道 <

5种经典排序算法,每个程序员都应该知道

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 有没有想过当您应用从低到高、从高到低或按字母顺序等过滤器时&#xff0c;亚马逊或任何其他电子商务网站中的产品如何排序&#xff1f;排序算法对于此类网站起着至…

分布式事务2PC二阶段提交详解

文章目录 概述和概念执行过程和工作流程特点优劣势应用场景总结demo代码样例 概述和概念 二阶段提交&#xff08;2PC&#xff09;是一种用于确保在分布式系统中的所有节点在进行事务提交时保持一致性的算法 二阶段提交&#xff08;Two-Phase Commit&#xff0c;2PC&#xff09…

PCIe surprise down异常与DPC功能分析-part1

在PCIe系统中&#xff0c;多个设备通过PCIe链路连接在一起&#xff0c;形成一个复杂的互连网络。这些设备可能包括CPU、GPU、网卡、存储控制器等。由于PCIe是一种高速、低延迟的总线标准&#xff0c;任何设备故障或错误都可能迅速传播到整个系统&#xff0c;导致数据损坏、系统…

跨平台Markdown编辑软件Typora mac功能介绍

Typora mac是一款跨平台的Markdown编辑器&#xff0c;支持Windows、MacOS和Linux操作系统。它具有实时预览功能&#xff0c;能够自动将Markdown文本转换为漂亮的排版效果&#xff0c;让用户专注于写作内容而不必关心格式调整。Typora Mac版除了支持常见的Markdown语法外&#x…

Spring Security 6.x 系列(12)—— Form表单认证登录注销自定义配置

一、前言 在本系列文章中介绍了 Form 表单认证和注销流程&#xff0c;对部分源码也进行详细分析。 本章主要学习 Spring Security 中表单认证登录注销的相关自定义配置。 二、自定义登录页面 Spring Security 表单认证默认规则中对未认证的请求会重定向到默认登录页面&…

Spring Boot学习随笔- 第一个Thymeleaf应用(基础语法th:,request、session作用域取值)

学习视频&#xff1a;【编程不良人】2021年SpringBoot最新最全教程 第十五章、Thymeleaf Thymeleaf是一种现代化的服务器端Java模板引擎&#xff0c;专门用于Web和独立环境。Thymeleaf在有网络和无网络的环境下皆可运行&#xff0c;即可以让美工在浏览器查看页面的静态效果&am…

概率中的 50 个具有挑战性的问题 [05/50]:正方形硬币

一、说明 我最近对与概率有关的问题产生了兴趣。我偶然读到了弗雷德里克莫斯特勒&#xff08;Frederick Mosteller&#xff09;的《概率论中的五十个具有挑战性的问题与解决方案》&#xff09;一书。我认为创建一个系列来讨论这些可能作为面试问题出现的迷人问题会很有趣。每篇…

Axios 中的文件上传 File对象的方法

使用 FormData 对象 FormData是一个用于创建表单数据的 API&#xff0c;可用于发送包含文件和其他表单数据的multipart/form-data请求。这是处理文件上传的常用方法。通过FormData对象&#xff0c;可以将文件数据添加到表单中&#xff0c;然后使用 Axios 的post或put方法发送请…