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

news2025/1/17 17:53:38

 1 定时器的使用

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

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

 

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

2 schedule(TimerTask task,Date time)方法

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

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

public class MyTask extends TimerTask {
    @Override
    public 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-empty
                    while (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 thing
                    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) { // Non-repeating, remove
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { // Repeating task, reschedule
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    if (!taskFired) // Task hasn't yet fired; wait
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks
                    task.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 {
    @Override
    public 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 {
    @Override
    public 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 {
    @Override
    public 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.coloradmin.cn/o/1331363.html

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

相关文章

UGC编辑器开发-代码实现物体旋转操作轴

1.视频效果&#xff1a; 工程百度网盘链接&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1OYkt2T3Wv_Hh0Bt7nLyR-A 提取码&#xff1a;1212 2.设计思路&#xff1a; 我们从鼠标点击的屏幕坐标打出一根射线&#xff0c;求出射线和旋转面的交点&#xff0c;交点减去原…

【大数据HA】HAProxy实现thrift协议HMS服务的高可用-附Chatgpt协助截图

背景 之前安装了HMS(Hive metastore service)&#xff0c;独立于hive运行&#xff0c;安装部署过程见我下面列出的另一篇文章&#xff0c;需要为它建立HA高可用功能。防止在访问时出现单点故障问题。 【大数据】Docker部署HMS(Hive Metastore Service)并使用Trino访问Minio-C…

PADS Layout安全间距检查报错

问题&#xff1a; 在Pads Layout完成layout后&#xff0c;进行工具-验证设计安全间距检查时&#xff0c;差分对BAK_FIXCLK_100M_P / BAK_FIXCLK_100M_N的安全间距检查报错&#xff0c;最小为3.94mil&#xff0c;但是应该大于等于5mil&#xff1b;如下两张图&#xff1a; 检查&…

Docker安装(CentOS)+简单使用

Docker安装(CentOS) 一键卸载旧的 sudo yum remove docker* 一行代码(自动安装) 使用官方安装脚本 curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun 启动 docker并查看状态 运行镜像 hello-world docker run hello-world 简单使用 使用 docker run …

自学华为鸿蒙开发?一般人我还是劝你算了吧!!!

本人纯屌丝一枚&#xff0c;在学编程之前对电脑的认知也就只限于上个网&#xff0c;玩个办公软件。这里不能跑题&#xff0c;我为啥说自学鸿蒙开发&#xff0c;一般人我还是劝你算了吧。因为我就是那个一般人。 基础真的很简单&#xff0c;是个人稍微认点真都能懂&#xff0c;…

【Java异常】聊聊异常可能带来的坑

一个活生生的案例 本周帮同事排查了一个问题&#xff0c;比较诡异的是他通过测试&#xff0c;并没有找到根本原因&#xff0c;只是发现有对应的错误日志。 但是其实并没有将堆栈信息打印出来。很难看出问题。添加了 e.printStackTrace(); get exception in exter: / by zero显…

JavaWeb—html, css, javascript, dom,xml, tomcatservlet

文章目录 快捷键HTML**常用特殊字符替代:****标题****超链接标签****无序列表、有序列表****无序列表**:ul/li 基本语法**有序列表ol/li:****图像标签(img)**** 表格(table)标签****表格标签-跨行跨列表格****form(表单)标签介绍****表单form提交注意事项**div 标签p 标签sp…

跟着LearnOpenGL学习9--光照

文章目录 一、颜色二、创建光照场景 一、颜色 显示世界中有无数种颜色&#xff0c;每一个物体都有它们自己的颜色。我们需要使用&#xff08;有限的&#xff09;数值来模拟现实世界中&#xff08;无限的&#xff09;的颜色&#xff0c;所以并不是所有现实世界中的颜色都可以用…

「数据结构」二叉树2

&#x1f387;个人主页&#xff1a;Ice_Sugar_7 &#x1f387;所属专栏&#xff1a;初阶数据结构 &#x1f387;欢迎点赞收藏加关注哦&#xff01; 文章目录 &#x1f349;前言&#x1f349;链式结构&#x1f349;遍历二叉树&#x1f34c;前序遍历&#x1f34c;中序遍历&#x…

年末汇总⭐️ 我是如何从学生切换到职场人身份的

目录 2023.12.23 天气阴 温度较低 一、Learning 二、Working 三、Living 章末 2023.12.23 天气阴 温度较低 小伙伴们大家好&#xff0c;冬已至 年将末 身为逮虾户的我看到大家的年末总结心中也不由得涌起一股创作热情&#xff0c;奈何没文化&#x…

qt简单连接摄像头

要使用摄像头&#xff0c;就需要链接多媒体模块以及多媒体工具模块 需要在.pro文件中添加QT multimedia multimediawidgets 是用的库文件 QCamera 类用于打开系统的摄像头设备&#xff0c; QCameraViewfinder 用于显示捕获的视频&#xff0c; QCameraImageCapt…

tcp vegas 为什么好

我吹捧 bbr 时曾论证过它在和 buffer 拧巴的时候表现如何优秀&#xff0c;但这一次说 vegas 时&#xff0c;我说的是从拥塞控制这个问题本身看来&#xff0c;vegas 为什么好&#xff0c;并且正确。 接着昨天 tcp vegas 鉴赏 继续扯。 假设一群共享带宽的流量中有流量退出或有…

每日一题:给定一个字符串s,请你找出其中不含有重复字符得最长子串的长度

每日一题&#xff1a;给定一个字符串s&#xff0c;请你找出其中不含有重复字符得最长子串的长度 function getLongSubstring(s){let map new Map();let max 0;let left 0;for(let i0;i<s.length;i){if(map.has(s[i]) && map.get(s[i])>left){left map.get(s…

D3839|完全背包

完全背包&#xff1a; 首先01背包的滚动数组中的解法是内嵌的循环是从大到小遍历&#xff0c;为了保证每个物品仅被添加一次。 for(int i 0; i < weight.size(); i) { // 遍历物品for(int j bagWeight; j > weight[i]; j--) { // 遍历背包容量dp[j] max(dp[j], dp[j…

在线渗透盒子,集成了近百个常见的渗透渗透工具,类似软件商城的工具可以进行工具下载

现在简单统计了一下大概有80个渗透工具左右&#xff0c;3个运行环境&#xff0c;1个破解工具 工具包介绍 该工具是一个类似软件商城的工具可以进行工具下载&#xff0c;工具的卸载&#xff0c;工具的更新&#xff0c;工具编写了自动化的安装脚本&#xff0c;不用担心工具跑不起…

oracle即时客户端(Instant Client)安装与配置

之前的文章记录了oracle客户端和服务端的下载与安装&#xff0c;内容参见&#xff1a; 在Windows中安装Oracle_windows安装oracle 如果不想安装oracle客户端&#xff08;或者是电脑因为某些原因无法安装oracle客户端&#xff09;&#xff0c;还想能够连接oracle远程服务&#…

【数据结构】递归与分治

一.递归 1.递归的概念&#xff1a; 子程序&#xff08;或函数&#xff09;. 接调用自己或通过一系列调用语句间接调用自己&#xff0c;成为递归。 递归是一种描述问题和解决问题的基本方法。 重复地把问题转化为与原问题相似的新问题&#xff0c;直到问题解决为止。 2.递归…

要参加微软官方 Copilot 智能编程训练营了

GitHub Copilot 是由 GitHub、OpenAI 和 Microsoft 联合开发的生成式 AI 模型驱动的。 GitHub Copilot 分析用户正在编辑的文件及相关文件的上下文&#xff0c;并在编写代码时提供自动补全式的建议。 刚好下周要参加微软官方组织的 GitHub Copilot 工作坊-智能编程训练营&…

Blender插件-The Grove 10 树木生长动画植物插件

注意&#xff1a;Blender和The Grove的版本匹配。 亲测Blender 2.9与The Grove 10可以配合使用&#xff0c;Blender 3.6会报错&#xff0c;具体看报错记录。 一、下载 CG咖官网地址&#xff1a; Blender插件-树木生长插件植物生成插件 The Grove 10插件资产库 CSDN下载地址…

AI技术图像编辑 Luminar Neo最新中文 for Mac

Luminar Neo是一款功能强大的AI智能图像处理工具&#xff0c;借助Luminar Neo领先的AI技术和灵活的工作流程&#xff0c;用户可以完成创意任务并获得专业品质的编辑结果。以下是该软件的主要特点和功能&#xff1a; 支持多种文件格式&#xff1a;Luminar Neo支持多种文件格式&…