java多线程之定时器

news2024/11/18 22:22:11

文章目录

  • 一、 简介
    • 1. 概念
    • 2. 定时器的使用
  • 二、 常用方法介绍
    • 1. Schedule(TimTask task,Data time)
    • 2. Schedule(TimTask task,Data firstTime, long period)
    • 3. Schedule(TimTask task, long delay)
    • 4. Schedule(TimTask task, long delay, long period)
    • 5. scheduleAtFixedRate(TimerTask task, Date firstTime,long period)

一、 简介

1. 概念

定时功能在java中主要是通过 Timer类实现,因为它在内部还是使用多线程的方式进行处理,所以和多线程技术还是有非常大的管理

2. 定时器的使用

在JDK库中Timer类主要负责计划任务的功能,也就是在指定时间开始执行某一个任务,Timer类的主要功能就是设置计划任务,封装任务的类确是TimerTask,因为TimerTask是一个抽象类,所以执行计划任务的代码要放入TimerTask子类中。

二、 常用方法介绍

1. Schedule(TimTask task,Data time)

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

  1. 执行任务的时间晚于当前时间(在未来执行)的效果

如果Data time,该参数的时间晚于现在的时间就是未来执行,而该参数的时间早于现在的执行就是立即执行的意思

测试代码如下

public class Main {
    public static void main(String[] args) throws InterruptedException {
        long nowTime=System.currentTimeMillis();
        System.out.println("当前时间为:"+nowTime);

        //计划时间比当前晚10s
        long scheduleTime=(nowTime+10000);
        Mytest task=new Mytest();
        Timer timer=new Timer();
        Thread.sleep(1000);
        timer.schedule(task,new Date(scheduleTime));

        Thread.sleep(Integer.MAX_VALUE);

    }
}
class Mytest extends TimerTask{
    @Override
    public void run() {
        System.out.println("任务执行了,时间为:"+System.currentTimeMillis());

    }

}

测试后你会发现过了一段时间才执行TimerTask中的任务

  1. 线程TimerThread不销毁的原因

其实上面10s后任务虽然执行完了,但是进程并没有销毁,因为创建Timer类的同时会创建一个新进程(TimerThread—实现计时器的任务执行线程,该线程等待计时器队列上的任务,在触发时执行它们,重新计划重复任务,并从队列中删除已取消的任务和已花费的非重复任务),Timer源码如下:

在这里插入图片描述
(图中应该是线程,有笔误)

而这线程是进程不结束的原因,因为它的run方法调用了一个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) {
            }
        }
    }
}
  • 使用while循环对queue.isempty()&&newTasksMayBescheduled条件进行判断
  • 当满足时,执行wait方法暂停该线程,等待被唤醒
  • 唤醒的时机是执行了public void schedule(TimeTask task ,Data time)方法,说明要执行新的任务了
  • 唤醒后while继续判断前面的条件,如果有新的任务被安排,则会向下继续执行
  • if(queue.isempty())判断结果为true的时候,说明队列为空,那么执行break退出死循环
  • 如果不执行public void cancel()方法会使布尔变量newTasksMayBeSchedule的值由true变为false,进程不销毁就是这个原因,cancel源码如
    public void cancel() {
        synchronized(queue) {
            thread.newTasksMayBeScheduled = false;
            queue.clear();
            queue.notify();  // In case queue was already empty.
        }
    }

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

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

创建的方法如下

//创建一个Timer
TImer timer= new Timer();
//创建多个任务
MyTask task1=new MyTask();
MyTask task2=new MyTask();
//执行schedule方法
timer.schedule(task1,new Date(scheduleTime1));
timer.schedule(task2,new Date(scheduleTime2));

由于多个任务的存在,因为TimerTask是以队列的方式一个一个被顺序执行的,所以执行时间有可能和预期的时间不一致,这是因为前面的任务可能执行时间过长,导致后面的任务被延时

2. Schedule(TimTask task,Data firstTime, long period)

改方法的作用是在指定的日期之后按指定的间隔周期无限地执行某一个任务,参数firstTime是循环任务开始的时间,period是任务循环的,和上面介绍的方法一样,该方法也有上面方法的特性:

  • 执行任务的时间晚于当前时间:任务会从指定时间开始,在指定的时间周期内开始循环执行任务
  • 执行任务的时间早于当前时间:任务同样会立即开始执行,且在指定的时间间隔内循环执行
  • 延时问题:同样该方法也会存在任务延时执行的问题
  • cancel方法:前面介绍了Timer类的cancel方法的特性它会终止所有没有执行的任务(清除队列中所有任务),而TimerTask类中的方法的作用是将自身从任务队列中消除,源码如下,可见该方法时将当前TimerTask任务的状态改为CANCELLED
   public boolean cancel() {
        synchronized(lock) {
            boolean result = (state == SCHEDULED);
            state = CANCELLED;
            return result;
        }
    }
  • 同一时间间隔内执行多个循环任务:算法是,当队列中有3个任务ABC时,这3个任务的执行顺序的算法是每次将最后一个任务放入队列头,在再执行队列头中任务的run方法。例如,ABC,CAB,BCA以此类推

3. Schedule(TimTask task, long delay)

该方法的作用是以执行schedule(Timer task, long delay)方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数后执行一次TimerTask任务

4. Schedule(TimTask task, long delay, long period)

该方法的作用是以当前时间为参考时间,在此基础上延迟指定的毫秒数,再以某一时间为间隔无限次数地执行某一任务。

5. scheduleAtFixedRate(TimerTask task, Date firstTime,long period)

schedule方法和scheduleAtFixedRate方法的主要区别在于有没有追赶特性

  1. 测试schedule方法任务不延时(Date类型-其实就是Schedule(TimTask task,Data firstTime, long period)

代码测试如下:

public class Main {
    public static void main(String[] args) throws InterruptedException {
         Mytest task=new Mytest();
         long nowTime=System.currentTimeMillis();
         Timer timer=new Timer();
         timer.schedule(task,new Date(nowTime),3000);

    }
}
class Mytest extends TimerTask{
    @Override
    public void run() {
       try{
           System.out.println("begin timer="+System.currentTimeMillis());
           Thread.sleep(1000);
           System.out.println("end timer"+System.currentTimeMillis());
       }catch (InterruptedException e) {
           e.printStackTrace();
       }

    }

}

在这里插入图片描述
由结果可以知道,在不延时的情况下,如果执行任务的时间没有被延时,则下一次执行任务的开始时间就是上一次任务的开始时间加上Period时间,所谓的“不延时”是指执行任务的时间小于或等于period时间间隔

  1. 测试schedule方法任务不延时(long类型-其实就是Schedule(TimTask task, long delay, long period)

代码测试如下:

public class Main {
    public static void main(String[] args) throws InterruptedException {
         Mytest task=new Mytest();
         long nowTime=System.currentTimeMillis();
         Timer timer=new Timer();
         timer.schedule(task,new 3000,3000);

    }
}
class Mytest extends TimerTask{
    @Override
    public void run() {
       try{
           System.out.println("begin timer="+System.currentTimeMillis());
           Thread.sleep(1000);
           System.out.println("end timer"+System.currentTimeMillis());
       }catch (InterruptedException e) {
           e.printStackTrace();
       }

    }

}

在这里插入图片描述
在不延时的情况下,如果执行任务的时间没有被延时,则第一次执行任务的时间是任务开始的时间加上被延迟的时间,接下来执行任务的时间是上一次的开始时间加上period时间

  1. 测试shceudle方法的任务延迟(Date类型)
public class Main {
    public static void main(String[] args) throws InterruptedException {
         Mytest task=new Mytest();
         long nowTime=System.currentTimeMillis();
         Timer timer=new Timer();
         timer.schedule(task,new Date(nowTime),2000);

    }
}
class Mytest extends TimerTask{
    @Override
    public void run() {
       try{
           System.out.println("begin timer="+System.currentTimeMillis());
           Thread.sleep(5000);
           System.out.println("end timer"+System.currentTimeMillis());
       }catch (InterruptedException e) {
           e.printStackTrace();
       }

    }

}

在这里插入图片描述
从控制台打印的结果可以看出,在延时的情况下,如果执行任务的时间被延时,那么下一次任务的执行时间参考的时上一次任务“结束”的时间,同样子long类型也是这样,同样scheduleAtFixedRate在long和date类型下在延迟和不延迟的情况下都是一样的

  1. 验证schedule方法不具有追赶执行性
    执行下面代码
public class Main {
    public static void main(String[] args) throws InterruptedException {
         Mytest task=new Mytest();
         long nowTime=System.currentTimeMillis();
        System.out.println("现在执行时间:"+nowTime);
        long runtime=nowTime-20000;
        System.out.println("计划执行时间:"+runtime);
        Timer timer=new Timer();
        timer.scheduleAtFixedRate(task,new Date(runtime),2000);

    }
}
class Mytest extends TimerTask{
    @Override
    public void run() {
           System.out.println("begin timer="+System.currentTimeMillis());
           System.out.println("end timer"+System.currentTimeMillis());
    }

}

在这里插入图片描述

由结果可以看出,计划在1684417340323和1684417320323之间的任务没有追赶执行,所谓的追赶执行,就是原来计划在前面那段时间的任务,由于时间已经过了,只能从现在开始给它补上来,这就是追赶。而该方法也有追赶执行性

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

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

相关文章

数据结构_排序

目录 1. 排序的基本概念 2. 插入排序 2.1 直接插入排序 2.2 折半插入排序 2.3 希尔排序 2.4 相关练习 3. 交换排序 3.1 冒泡排序 3.2 快速排序 3.3 相关练习 4. 选择排序 4.1 简单选择序列 4.2 堆排序 4.3 相关练习 5. 归并排序和基数排序 5.1 归并排序 5.2 基…

这几款实用的电脑软件推荐给你

软件一&#xff1a;TeamViewer TeamViewer是一款跨平台的远程控制软件&#xff0c;它可以帮助用户远程访问和控制其他计算机、服务器、移动设备等&#xff0c;并且支持文件传输、会议功能等。 TeamViewer的主要功能包括&#xff1a; 远程控制&#xff1a;支持远程访问和控制…

【Jenkins】Jenkins运行python脚本的简单操作(windows)

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化 &#x1f449;专__注&#x1f448;&#xff1a;专注主流机器人、人工智能等相关领域的开发、…

Vue基础入门(中)

组件的定义 组件具备复用性 注意&#xff1a;全局组件&#xff0c;只要定义了&#xff0c;处处可以使用&#xff0c;性能不高&#xff0c;但是使用起来简单 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><me…

Linux下的用户分类与su/sudo 命令,Linux下的文件类型/用户文件权限身份/文件权限属性/权限与文件权限/ls-l文件属性详解

Tips 下载就是把我们的文件拷贝到系统的某个特定路径之下&#xff0c;普通用户是不允许你往系统里面去拷的。 Linux下的用户分类 root用户&#xff0c;管理员级别的用户身份&#xff0c;他的话基本上不受权限的约束。普通用户&#xff0c;普通用户的添加与每个普通用户密码的…

《面试1v1》JavaNIO

我是 javapub&#xff0c;一名 Markdown 程序员从&#x1f468;‍&#x1f4bb;&#xff0c;八股文种子选手。 面试官&#xff1a; 你好&#xff0c;我想问一下你对 Java NIO 的了解。 候选人&#xff1a; 当然&#xff0c;Java NIO 是 Java 的一种 I/O 模型&#xff0c;它提…

MapReduce【自定义分区Partitioner】

实际开发中我们可能根据需求需要将MapReduce的运行结果生成多个不同的文件&#xff0c;比如上一个案例【MapReduce计算广州2022年每月最高温度】&#xff0c;我们需要将前半年和后半年的数据分开写到两个文件中。 默认分区 默认MapReduce只能写出一个文件&#xff1a; 因为我…

「OceanBase 4.1 体验」OceanBase:解读领先的分布式数据库系统,功能与体验全解析

文章目录 前言一、关于 【OceanBase 4.1】征文活动&#xff08;可跳过&#xff09;二、OceanBase 产品了解2.1 初识 OceanBase2.2 什么是 OceanBase2.3 OceanBase 相关链接2.4 OceanBase 与传统数据库对比有何特别之处2.5 OceanBase 相关概念以及术语2.5.1 OceanBase 基本概念2…

接口测试总结及其用例设计方法整理,希望可以帮到你

目录 接口测试的总结文档 第一部分&#xff1a; 第二部分&#xff1a; 接口测试用例设计 接口测试的总结文档 第一部分&#xff1a;主要从问题出发&#xff0c;引入接口测试的相关内容并与前端测试进行简单对比&#xff0c;总结两者之前的区别与联系。但该部分只交代了怎么做…

ios中video标签兼容问题

案例 这是在实际开发中遇到的问题,产品给出的效果图如下: 其实就是一个h5页面中有很多视频, 但是我为什么都画着预览图呢? 因为在ios中video标签中尽管有src属性, 但是在没有预览图的情况下, 是一个灰蒙蒙的图层的, 很丑, 效果如下: 看到这里是不是很崩溃, 在PC端, 在安卓端…

RK3568平台开发系列讲解(Camera篇)快速了解RK Camera的使用

🚀返回专栏总目录 文章目录 一、MIPI CSI1.1 Full Mode特点1.2 Split Mode特点二、Full Mode配置2.1 配置sensor端2.2 csi2_dphy0相关配置2.3 isp相关配置三、Split Mode配置3.1 配置sensor端3.2 csi2_dphy1/csi2_dphy2相关配置3.3 isp相关配置四、软件相关目录

自然语言处理实战项目7-利用层次聚类方法做文本的排重,从大量的文本中找出相似文本

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下自然语言处理实战项目7-利用层次聚类方法做文本的排重&#xff0c;从大量的文本中找出相似文本。随着互联网技术的不断发展&#xff0c;越来越多的数据被广泛地应用在各个领域中。而文本数据是其中之一&#xff0c;…

ar在汽车维修行业的应用场景

由于AR增强现实技术的易用性&#xff0c;在汽车产业链中&#xff0c;已处处可见AR技术的踪影&#xff0c;像汽车设计AR远程协同&#xff0c;汽车装配AR远程指导&#xff0c;汽车维修AR远程协助等等&#xff0c;那么下面为详细介绍AR增强现实技术在汽车制造领域的应用。 环境/物…

实力在线 | 赛宁网安连续三年入选《中国网络安全市场全景图》

​​近日&#xff0c;国内专业安全机构数说安全正式发布了《2023年中国网络安全市场全景图》&#xff08;以下简称全景图&#xff09;。此次全景图分类架构涵盖了产品、解决方案、应用场景、服务4个维度&#xff0c;覆盖了目前我国网络安全行业所有成熟的细分市场。 赛宁网安凭…

Postman实现数字签名,Session依赖, 接口依赖, 异步接口结果轮询

Script(JS)为Postman赋予无限可能 基于Postman 6.1.4 Mac Native版 演示结合user_api_demo实现 PS 最近接到任务, 要把几种基本下单接口调试和持续集成一下, 做个常规功能验证, 研究了下发现, 不同的下单途径, 有的需要登录(Session依赖), 有的需要验签(使用数字签名的微服务),…

用本地机做跳板使服务器连接外网【mac】

用自己的电脑做跳板使服务器连接外网 前提整体流程连接服务器配置服务器配置自己的电脑 前提 很多时候我们的服务器只能联内网&#xff0c;但是没法登外网&#xff0c;这样pip&#xff0c;conda 啥的都没法用&#xff0c;很麻烦。 一个简单的解决方法就是用自己的电脑作为跳板…

补充点【数据分析统计学知识点】

数据分析所需的统计学笔记 0.数据分析师拿到数据 第一利器&#xff1a;描述性统计信息 第二利器&#xff1a;对比分析法 第三利器&#xff1a;指标体系-要素拆解&#xff0c;维度拆解 第四利器&#xff1a;相关分析 第五利器&#xff1a;趋势预测 1.均值 2.中位数 3.众数…

基于Java+SpringBoot+vue的口腔管家平台设计与实现

博主介绍&#xff1a;擅长Java、微信小程序、Python、Android等&#xff0c;专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不然下次找不到哟 Java项目精品实战案例…

Hadoop 3.0.0的集群搭建和Hive 3.1.3的安装

Hi,朋友们&#xff0c;我们又见面了&#xff0c;这一次我给大家带来的是Hadoop集群搭建及Hive的安装。 说明一下&#xff0c;网上有很多的教程&#xff0c;为什么我还要水&#xff1f; 第一&#xff0c;大多数的安装都是很顺利的&#xff0c;没有疑难解答。 第二&#xff0c…

分布式项目08 redis的扩容,预备redis宕机的哨兵处理 和 最后集大成redis集群

01.redis扩容 由于redis的容量也是有限的&#xff0c;所以&#xff0c;就会有扩容的操作。也就好像内存中扩容一样。redis有分片这种说法&#xff0c;具体实现操作如下&#xff1a; 第一步&#xff1a;首先在/usr/local/src中去找到redis文件夹。cd /usr/local/src 而后进入re…