`
wusuoya
  • 浏览: 643171 次
  • 性别: Icon_minigender_2
  • 来自: 成都
社区版块
存档分类
最新评论

详解java定时任务

    博客分类:
  • Java
 
阅读更多

在我们编程过程中如果需要执行一些简单的定时任务,无须做复杂的控制,我们可以考虑使用JDK中的Timer定时任务来实现。下面LZ就其原理、实例以及Timer缺陷三个方面来解析java Timer定时器。

一、简介

在java中一个完整定时任务需要由Timer、TimerTask两个类来配合完成。 API中是这样定义他们的,Timer:一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。由TimerTask:Timer 安排为一次执行或重复执行的任务。我们可以这样理解Timer是一种定时器工具,用来在一个后台线程计划执行指定任务,而TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务。

Timer类

在工具类Timer中,提供了四个构造方法,每个构造方法都启动了计时器线程,同时Timer类可以保证多个线程可以共享单个Timer对象而无需进行外部同步,所以Timer类是线程安全的。但是由于每一个Timer对象对应的是单个后台线程,用于顺序执行所有的计时器任务,一般情况下我们的线程任务执行所消耗的时间应该非常短,但是由于特殊情况导致某个定时器任务执行的时间太长,那么他就会“独占”计时器的任务执行线程,其后的所有线程都必须等待它执行完,这就会延迟后续任务的执行,使这些任务堆积在一起,具体情况我们后面分析。

当程序初始化完成Timer后,定时任务就会按照我们设定的时间去执行,Timer提供了schedule方法,该方法有多中重载方式来适应不同的情况,如下:

schedule(TimerTask task, Date time):安排在指定的时间执行指定的任务。

schedule(TimerTask task, Date firstTime, long period) :安排指定的任务在指定的时间开始进行重复的固定延迟执行。

schedule(TimerTask task, long delay) :安排在指定延迟后执行指定的任务。

schedule(TimerTask task, long delay, long period) :安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。

同时也重载了scheduleAtFixedRate方法,scheduleAtFixedRate方法与schedule相同,只不过他们的侧重点不同,区别后面分析。

scheduleAtFixedRate(TimerTask task, Date firstTime, long period):安排指定的任务在指定的时间开始进行重复的固定速率执行。

scheduleAtFixedRate(TimerTask task, long delay, long period):安排指定的任务在指定的延迟后开始进行重复的固定速率执行。

TimerTask

TimerTask类是一个抽象类,由Timer 安排为一次执行或重复执行的任务。它有一个抽象方法run()方法,该方法用于执行相应计时器任务要执行的操作。因此每一个具体的任务类都必须继承TimerTask,然后重写run()方法。

另外它还有两个非抽象的方法:

boolean cancel():取消此计时器任务。

long scheduledExecutionTime():返回此任务最近实际执行的安排执行时间。

二、实例

2.1、指定延迟时间执行定时任务

复制代码
public class TimerTest01 {
    Timer timer;
    public TimerTest01(int time){
        timer = new Timer();
        timer.schedule(new TimerTaskTest01(), time * 1000);
    }
    
    public static void main(String[] args) {
        System.out.println("timer begin....");
        new TimerTest01(3);
    }
}

public class TimerTaskTest01 extends TimerTask{

    public void run() {
        System.out.println("Time's up!!!!");
    }
}
复制代码

运行结果:

首先打印:timer begin....

3秒后打印:Time's up!!!!

2.2、在指定时间执行定时任务

复制代码
public class TimerTest02 {
    Timer timer;
    
    public TimerTest02(){
        Date time = getTime();
        System.out.println("指定时间time=" + time);
        timer = new Timer();
        timer.schedule(new TimerTaskTest02(), time);
    }
    
    public Date getTime(){
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.HOUR_OF_DAY, 11);
        calendar.set(Calendar.MINUTE, 39);
        calendar.set(Calendar.SECOND, 00);
        Date time = calendar.getTime();
        
        return time;
    }
    
    public static void main(String[] args) {
        new TimerTest02();
    }
}

public class TimerTaskTest02 extends TimerTask{

    @Override
    public void run() {
        System.out.println("指定时间执行线程任务...");
    }
}
复制代码

当时间到达11:39:00时就会执行该线程任务,当然大于该时间也会执行!!执行结果为:

指定时间time=Tue Jun 10 11:39:00 CST 2014
指定时间执行线程任务...

2.3、在延迟指定时间后以指定的间隔时间循环执行定时任务

复制代码
public class TimerTest03 {
    Timer timer;
    
    public TimerTest03(){
        timer = new Timer();
        timer.schedule(new TimerTaskTest03(), 1000, 2000);
    }
    
    public static void main(String[] args) {
        new TimerTest03();
    }
}

public class TimerTaskTest03 extends TimerTask{

    @Override
    public void run() {
        Date date = new Date(this.scheduledExecutionTime());
        System.out.println("本次执行该线程的时间为:" + date);
    }
}
复制代码

运行结果:

复制代码
本次执行该线程的时间为:Tue Jun 10 21:19:47 CST 2014
本次执行该线程的时间为:Tue Jun 10 21:19:49 CST 2014
本次执行该线程的时间为:Tue Jun 10 21:19:51 CST 2014
本次执行该线程的时间为:Tue Jun 10 21:19:53 CST 2014
本次执行该线程的时间为:Tue Jun 10 21:19:55 CST 2014
本次执行该线程的时间为:Tue Jun 10 21:19:57 CST 2014
.................
复制代码

对于这个线程任务,如果我们不将该任务停止,他会一直运行下去。

对于上面三个实例,LZ只是简单的演示了一下,同时也没有讲解scheduleAtFixedRate方法的例子,其实该方法与schedule方法一样!

2.4、分析schedule和scheduleAtFixedRate

1、schedule(TimerTask task, Date time)、schedule(TimerTask task, long delay)

对于这两个方法而言,如果指定的计划执行时间scheduledExecutionTime<= systemCurrentTime,则task会被立即执行。scheduledExecutionTime不会因为某一个task的过度执行而改变。

2、schedule(TimerTask task, Date firstTime, long period)、schedule(TimerTask task, long delay, long period)

这两个方法与上面两个就有点儿不同的,前面提过Timer的计时器任务会因为前一个任务执行时间较长而延时。在这两个方法中,每一次执行的task的计划时间会随着前一个task的实际时间而发生改变,也就是scheduledExecutionTime(n+1)=realExecutionTime(n)+periodTime。也就是说如果第n个task由于某种情况导致这次的执行时间过程,最后导致systemCurrentTime>= scheduledExecutionTime(n+1),这是第n+1个task并不会因为到时了而执行,他会等待第n个task执行完之后再执行,那么这样势必会导致n+2个的执行实现scheduledExecutionTime放生改变即scheduledExecutionTime(n+2) = realExecutionTime(n+1)+periodTime。所以这两个方法更加注重保存间隔时间的稳定。

3、scheduleAtFixedRate(TimerTask task, Date firstTime, long period)、scheduleAtFixedRate(TimerTask task, long delay, long period)

在前面也提过scheduleAtFixedRate与schedule方法的侧重点不同,schedule方法侧重保存间隔时间的稳定,而scheduleAtFixedRate方法更加侧重于保持执行频率的稳定。为什么这么说,原因如下。在schedule方法中会因为前一个任务的延迟而导致其后面的定时任务延时,而scheduleAtFixedRate方法则不会,如果第n个task执行时间过长导致systemCurrentTime>= scheduledExecutionTime(n+1),则不会做任何等待他会立即执行第n+1个task,所以scheduleAtFixedRate方法执行时间的计算方法不同于schedule,而是scheduledExecutionTime(n)=firstExecuteTime +n*periodTime,该计算方法永远保持不变。所以scheduleAtFixedRate更加侧重于保持执行频率的稳定。

三、Timer的缺陷

3.1、Timer的缺陷

Timer计时器可以定时(指定时间执行任务)、延迟(延迟5秒执行任务)、周期性地执行任务(每隔个1秒执行任务),但是,Timer存在一些缺陷。首先Timer对调度的支持是基于绝对时间的,而不是相对时间,所以它对系统时间的改变非常敏感。其次Timer线程是不会捕获异常的,如果TimerTask抛出的了未检查异常则会导致Timer线程终止,同时Timer也不会重新恢复线程的执行,他会错误的认为整个Timer线程都会取消。同时,已经被安排单尚未执行的TimerTask也不会再执行了,新的任务也不能被调度。故如果TimerTask抛出未检查的异常,Timer将会产生无法预料的行为。

1、Timer管理时间延迟缺陷

前面Timer在执行定时任务时只会创建一个线程任务,如果存在多个线程,若其中某个线程因为某种原因而导致线程任务执行时间过长,超过了两个任务的间隔时间,会发生一些缺陷:

复制代码
public class TimerTest04 {
    private Timer timer;
    public long start;   
    
    public TimerTest04(){
        this.timer = new Timer();
        start = System.currentTimeMillis();
    }
    
    public void timerOne(){
        timer.schedule(new TimerTask() {
            public void run() {
                System.out.println("timerOne invoked ,the time:" + (System.currentTimeMillis() - start));
                try {
                    Thread.sleep(4000);    //线程休眠3000
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 1000);
    }
    
    public void timerTwo(){
        timer.schedule(new TimerTask() {
            public void run() {
                System.out.println("timerOne invoked ,the time:" + (System.currentTimeMillis() - start));
            }
        }, 3000);
    }
    
    public static void main(String[] args) throws Exception {
        TimerTest04 test = new TimerTest04();
        
        test.timerOne();
        test.timerTwo();
    }
}
复制代码

按照我们正常思路,timerTwo应该是在3s后执行,其结果应该是:

timerOne invoked ,the time:1001
timerOne invoked ,the time:3001

但是事与愿违,timerOne由于sleep(4000),休眠了4S,同时Timer内部是一个线程,导致timeOne所需的时间超过了间隔时间,结果:

timerOne invoked ,the time:1000
timerOne invoked ,the time:5000

2、Timer抛出异常缺陷

如果TimerTask抛出RuntimeException,Timer会终止所有任务的运行。如下:

复制代码
public class TimerTest04 {
    private Timer timer;
    
    public TimerTest04(){
        this.timer = new Timer();
    }
    
    public void timerOne(){
        timer.schedule(new TimerTask() {
            public void run() {
                throw new RuntimeException();
            }
        }, 1000);
    }
    
    public void timerTwo(){
        timer.schedule(new TimerTask() {
            
            public void run() {
                System.out.println("我会不会执行呢??");
            }
        }, 1000);
    }
    
    public static void main(String[] args) {
        TimerTest04 test = new TimerTest04();
        test.timerOne();
        test.timerTwo();
    }
}
复制代码

运行结果:timerOne抛出异常,导致timerTwo任务终止。

Exception in thread "Timer-0" java.lang.RuntimeException
    at com.chenssy.timer.TimerTest04$1.run(TimerTest04.java:25)
    at java.util.TimerThread.mainLoop(Timer.java:555)
    at java.util.TimerThread.run(Timer.java:505)

对于Timer的缺陷,我们可以考虑 ScheduledThreadPoolExecutor 来替代。Timer是基于绝对时间的,对系统时间比较敏感,而ScheduledThreadPoolExecutor 则是基于相对时间;Timer是内部是单一线程,而ScheduledThreadPoolExecutor内部是个线程池,所以可以支持多个任务并发执行。

3.2、用ScheduledExecutorService替代Timer

1、解决问题一:

复制代码
public class ScheduledExecutorTest {
    private  ScheduledExecutorService scheduExec;
    
    public long start;
    
    ScheduledExecutorTest(){
        this.scheduExec =  Executors.newScheduledThreadPool(2);  
        this.start = System.currentTimeMillis();
    }
    
    public void timerOne(){
        scheduExec.schedule(new Runnable() {
            public void run() {
                System.out.println("timerOne,the time:" + (System.currentTimeMillis() - start));
                try {
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },1000,TimeUnit.MILLISECONDS);
    }
    
    public void timerTwo(){
        scheduExec.schedule(new Runnable() {
            public void run() {
                System.out.println("timerTwo,the time:" + (System.currentTimeMillis() - start));
            }
        },2000,TimeUnit.MILLISECONDS);
    }
    
    public static void main(String[] args) {
        ScheduledExecutorTest test = new ScheduledExecutorTest();
        test.timerOne();
        test.timerTwo();
    }
}
复制代码

运行结果:

timerOne,the time:1003
timerTwo,the time:2005

2、解决问题二

复制代码
public class ScheduledExecutorTest {
    private  ScheduledExecutorService scheduExec;
    
    public long start;
    
    ScheduledExecutorTest(){
        this.scheduExec =  Executors.newScheduledThreadPool(2);  
        this.start = System.currentTimeMillis();
    }
    
    public void timerOne(){
        scheduExec.schedule(new Runnable() {
            public void run() {
                throw new RuntimeException();
            }
        },1000,TimeUnit.MILLISECONDS);
    }
    
    public void timerTwo(){
        scheduExec.scheduleAtFixedRate(new Runnable() {
            public void run() {
                System.out.println("timerTwo invoked .....");
            }
        },2000,500,TimeUnit.MILLISECONDS);
    }
    
    public static void main(String[] args) {
        ScheduledExecutorTest test = new ScheduledExecutorTest();
        test.timerOne();
        test.timerTwo();
    }
}
复制代码

运行结果:

复制代码
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
timerTwo invoked .....
........................
复制代码

 

参考文献:http://blog.csdn.net/lmj623565791/article/details/27109467

分享到:
评论

相关推荐

    java写定时任务,定时任务、定时器详细示例

    Java中的定时任务与定时器是实现自动化操作的关键技术之一,特别是在需要周期性执行某些功能的场景下,如数据同步、日志清理、定时发送邮件等。本文将深入探讨如何使用Java标准库中的`java.util.Timer`和`java.util....

    详解SpringBoot 创建定时任务(配合数据库动态执行)

    为了实现动态定时任务,项目需要引入相关的依赖,如spring-boot-starter-web、mybatis-spring-boot-starter、mysql-connector-java等。这些依赖分别对应SpringBoot的Web模块、Mybatis集成模块以及MySQL数据库连接...

    Quartz定时任务表达式

    ### Quartz定时任务表达式详解 在探讨Quartz定时任务表达式的使用之前,我们先了解下Quartz是什么。Quartz是一个开源的作业调度框架,能够触发在指定时间运行的任务,广泛应用于Java应用程序中进行定时任务的管理。...

    玩转SpringBoot之定时任务详解

    ### 玩转Spring Boot之定时任务详解 #### 序言 在现代软件开发中,定时任务是非常重要的功能之一,特别是在后台服务系统中。通过定时任务可以实现诸如定期备份数据、定时发送邮件通知等需求。Spring Boot框架因其...

    springboot 定时任务加layui的界面版

    【SpringBoot定时任务与Layui界面集成详解】 在现代Web开发中,SpringBoot因其简洁、高效的特性,已经成为Java开发者构建后端服务的首选框架。而定时任务是系统功能的重要组成部分,它允许开发者安排任务在特定时间...

    Timer定时任务

    《Timer定时任务详解》 在计算机编程中,定时任务是一项重要的功能,它允许程序在特定的时间间隔或预定的时间点执行特定的操作。Java中的`java.util.Timer`类和`java.util.TimerTask`类为我们提供了实现定时任务的...

    基于SSM+quartz的定时任务管理demo

    【SSM+Quartz 定时任务管理Demo详解】 在Java Web开发中,定时任务是一项常见且重要的功能,用于执行一些周期性或者特定...同时,通过学习这个Demo,开发者可以深入理解Java定时任务的实现方式,提升项目开发能力。

    定时任务,JSP

    在Web开发中,定时任务常常用于实现后台的自动化操作,如数据备份、清理过期数据、发送邮件等,而JSP(JavaServer Pages)是常用的动态网页技术,用于展示前端界面。将两者结合,可以实现在JSP页面上进行定时任务的...

    Java定时任务详解

    Java定时任务是软件开发中不可或缺的一部分,特别是在大型企业级应用中,它们用于自动化执行一系列预定义的任务,如数据同步、报表生成、清理日志等。本文将深入探讨Quartz定时任务及其与Spring的集成。 首先,...

    C#定时调度任务服务

    其设计灵感来源于Java的Quartz项目,提供了强大的定时任务管理和并发处理能力。Quartz.NET通过Job和Trigger的概念,实现了任务(Job)和触发器(Trigger)的解耦,使得我们可以灵活地定义任务执行的时机和频率。 接...

    spring 定时任务配置详解

    Spring 定时任务配置详解 Spring 框架提供了对定时调度服务的内置支持类,支持从 JDK 1.3 开始内置的 Timer 类和 Quartz Scheduler。开发者可以通过 FactoryBean,分别指向 Timer 或 Trigger 实例的引用进行配置。...

    Spring Boot中的@Scheduled注解:定时任务的原理与实现

    相比于传统的Java Timer或者第三方定时任务库如Quartz等,`@Scheduled`提供了更为简洁和易于集成的方式来实现定时任务的需求。 **2.2 实现原理** `@Scheduled`注解的实现原理主要依赖于Spring框架的任务调度机制。...

    java定时任务Timer和TimerTask使用详解

    Java中的定时任务是程序开发中常见的一种需求,用于在特定的时间点或按固定频率执行某项操作。在Java中,`java.util.Timer` 和 `java.util.TimerTask` 类提供了这样的功能,它们是JDK自带的,不需要引入额外的库。 ...

    spring多个定时任务quartz配置.md

    ### Spring 多个定时任务 Quartz 配置详解 #### 一、Spring与Quartz集成概述 Spring 是一款轻量级的 Java 开发框架,而 Quartz 是一个强大的作业调度框架。两者结合可以方便地实现定时任务的功能。通过 Spring 来...

    定时任务quartz及教程

    Quartz是Java领域中广泛应用的一款开源的作业调度框架,它允许开发者在应用程序中安排复杂的定时任务。本教程将深入探讨Quartz的核心概念、配置、API使用以及如何在实际项目中集成和管理定时任务。 一、Quartz核心...

    Spring普通定时任务和动态设置定时任务

    ### Spring 普通定时任务与动态设置定时任务详解 #### 一、Spring 定时任务简介 在软件开发过程中,经常会遇到需要周期性执行的任务,例如数据备份、定时发送邮件等。对于这类需求,Spring 提供了一种简单且灵活的...

    配置为Windows定时任务

    ### 配置Windows定时任务详解 #### 一、概述 在Windows操作系统中,无论是Windows XP还是Windows 7,用户都可以通过“任务计划程序”来设置定时任务,以便于自动化执行某些脚本或程序。这对于系统管理员来说是非常...

Global site tag (gtag.js) - Google Analytics