Java定时任务调度:用ExecutorService取代Timer

标签: java 任务 调度 | 发表时间:2013-11-25 18:06 | 作者:
出处:http://www.iteye.com
《Java并发编程》一书提到,用ExecutorService取代Java Timer有几个理由,我认为其中最重要的理由是:
如果TimerTask抛出未检查的异常,Timer将会产生无法预料的行为。Timer线程并不捕获异常,所以 TimerTask抛出的未检查的异常会终止timer线程。这种情况下,Timer也不会再重新恢复线程的执行了;它错误的认为整个Timer都被取消了。此时,已经被安排但尚未执行的TimerTask永远不会再执行了,新的任务也不能被调度了。

stackoverflow上也有关于此问题的讨论:
http://stackoverflow.com/questions/409932/java-timer-vs-executorservice

Timer的问题:
package com.ljn.timer;

import java.util.Date;
import java.util.Timer;

/**
 * @author lijinnan
 * @date:2013-11-25 下午3:27:43  
 */
public class TimerException {

    public static void main(String[] args) {
        System.out.println("start:" + new Date());
        Timer timer = new Timer();
        int delay = 1000;
        int period = 2000;
        timer.schedule(new OKTask(), delay * 2, period);    //"OKTask" does not get chance to execute
        timer.schedule(new ErrorTask(), delay, period);  //exception in "ErrorTask" will terminate the Timer
    }
    
    /*输出:
start:Mon Nov 25 17:49:53 CST 2013
ErrorTask is executing...
error:Mon Nov 25 17:49:55 CST 2013
Exception in thread "Timer-0" java.lang.RuntimeException: something wrong
    at com.ljn.timer.ErrorTask.run(ErrorTask.java:14)
     */

 }



用ExecutorService则正常:
package com.ljn.timer;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author lijinnan
 * @date:2013-11-25 下午3:35:39  
 */
public class ScheduledExecutorServiceTest {

    public static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);  

    public static void main(String[] args){
        System.out.println("start:" + new Date());
        ErrorTask errorTask = new ErrorTask();
        OKTask okTask = new OKTask();
        int delay = 1000;
        int period = 2000;
        scheduledExecutorService.scheduleAtFixedRate(errorTask, delay, period, TimeUnit.MILLISECONDS);   //"ErrorTask" throws Exception and then stopes.
        scheduledExecutorService.scheduleAtFixedRate(okTask, delay * 2, period, TimeUnit.MILLISECONDS);     //"OKTask" is executed periodically, not affected by "ErrorTask"
        
        //scheduledExecutorService.shutdown();
    }

    /*
start:Mon Nov 25 17:54:22 CST 2013
ErrorTask is executing...
error occurs:Mon Nov 25 17:54:24 CST 2013
OKTask is executed:Mon Nov 25 17:54:24 CST 2013
OKTask is executed:Mon Nov 25 17:54:26 CST 2013
OKTask is executed:Mon Nov 25 17:54:28 CST 2013
......
     */

}



另外开发中常常会让任务在每天的指定时间点运行,示例如下:
package com.ljn.timer;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author lijinnan
 * @date:2013-11-25 下午5:18:55  
 */
public class FixedDatetimeTaskTest {

    public static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);  

    public static void main(String[] args) throws Exception {
        System.out.println("start:" + new Date());
        
        //每天的02:30:00执行任务
        long delay = Helper.calcDelay(2, 30, 0);
        long period = Helper.ONE_DAY;
        scheduledExecutorService.scheduleAtFixedRate(new OKTask(), delay, period, TimeUnit.MILLISECONDS); 
    }

}



文章中用到的其他类:
package com.ljn.timer;

import java.util.Date;
import java.util.TimerTask;

public class ErrorTask extends TimerTask {

     @Override
     public void run() {
         try {
             System.out.println("ErrorTask is executing...");
             Thread.sleep(1000);
             System.out.println("error occurs:" + new Date());
             throw new RuntimeException("something wrong");
         } catch (InterruptedException e) {
         }
     }
     
 }


package com.ljn.timer;

import java.util.Date;
import java.util.TimerTask;

 
public  class OKTask extends TimerTask {
     
     @Override
     public void run() {
         System.out.println("OKTask is executed:" + new Date());
     }
}


package com.ljn.timer;

import org.joda.time.DateTime;

/**
 * @author lijinnan
 * @date:2013-11-25 下午5:17:40  
 */
public class Helper {

    private Helper() {}
    
    public static final long ONE_DAY = 60 * 60 * 24;
    
    public static long calcDelay(int hour, int minute, int second) {
        if (!(0 <= hour && hour <=23 && 0 <= minute && minute <=59 && 0 <=second && second <= 59)) {
            throw new IllegalArgumentException();
        }
        return calcDelay(fixed(hour, minute, second));
    }
    
    private static long calcDelay(DateTime targetDatetimeOfToday) {
        long delay = 0;
        DateTime now = new DateTime();
        
        //时间点已过,只好延时到明天的这个时间点再执行
        if (now.isAfter(targetDatetimeOfToday)) {
            delay = now.plusDays(1).getMillis() - now.getMillis();
            
        //时间点未到
        } else {
            delay = targetDatetimeOfToday.getMillis() - now.getMillis();
        }
        
        return delay;
    }
    
    /**
     * 返回这样一个DateTime对象:
     * 1.日期为今天
     * 2.时分秒为参数指定的值
     * @param hour 0-23
     * @param minute 0-59
     * @param second 0-59
     * @return
     */
    private static DateTime fixed(int hour, int minute, int second) {
        
        return new DateTime()
                    .withHourOfDay(hour).withMinuteOfHour(minute).withSecondOfMinute(second);
    }

}



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java 任务 调度] 推荐:

几种任务调度的 Java 实现方法与比较

- wangyegang - IBM developerWorks 中国 : 文档库
综观目前的 Web 应用,多数应用都具备任务调度的功能. 本文由浅入深介绍了几种任务调度的 Java 实现方法,包括 Timer,Scheduler, Quartz 以及 JCron Tab,并对其优缺点进行比较,目的在于给需要开发任务调度的程序员提供有价值的参考.

Java定时任务调度:用ExecutorService取代Timer

- - ITeye博客
《Java并发编程》一书提到,用ExecutorService取代Java Timer有几个理由,我认为其中最重要的理由是:. 如果TimerTask抛出未检查的异常,Timer将会产生无法预料的行为. Timer线程并不捕获异常,所以 TimerTask抛出的未检查的异常会终止timer线程. 这种情况下,Timer也不会再重新恢复线程的执行了;它错误的认为整个Timer都被取消了.

Java Spring注解任务调度并实现AOP监控任务执行情况

- - 极客521 | 极客521
本文讲的是通过Spring注解的方式实现任务调度. 只要引入了spring-context包就能够在项目中使用注解方式的任务调度. 需要在Spring配置文件中加入task的schema. 然后在代码中就可以直接用了,要定时执行的方法必须是void的,并且没有任何参数的. cron表达式请自行问百度,下面只列出几个从网上找的例子.

Java 任务处理

- - 码蜂笔记
最近梳理其他同事以前写的 job 后有点想法,记录下. 在大多数的系统都有类似这样的逻辑,比如下单了给用户赠送积分,用户在论坛上发表了帖子,给用户增加积分等等. 下单赠送积分,那么一个订单肯定不能重复赠送积分,所以需要一些状态来比较来哪些是已赠送的,哪些是没有赠送的. 或许可以在订单表里加个字段来标记是否赠送了积分.

Spark 任务调度

- - IT瘾-dev
 Spark的核心是基于RDD来实现的,Spark任务调度就是如何组织任务去处理RDD中每个分区的数据,根据RDD的依赖关系构建DAG,基于DAG划分Stage,然后将每个Stage中的任务(Task)分发到指定的节点去运行得到最终的结果. Application:用户编写的Spark应用程序,由一个或多个Job组成.

使用Quartz和Obsidian来调度任务

- - Java译站
在介绍使用到的Quartz和Obsidian的API之前,首先我得声明一下,一般来说使用API并不是调度任务的最佳方式. Quartz提供了一个通过XML来配置作业的机制,而Obsidian则为你提供了一套完整的管理和监控的WEB应用. 然而,有一些使用场景还是强烈推荐使用API的,我们来看一下吧.

MapReduce调度与执行原理之任务调度

- - CSDN博客云计算推荐文章
前言:本文旨在理清在Hadoop中一个MapReduce作业(Job)在提交到框架后的整个生命周期过程,权作总结和日后参考,如有问题,请不吝赐教. 本文不涉及Hadoop的架构设计,如有兴趣请参考相关书籍和文献. 在梳理过程中,我对一些感兴趣的源码也会逐行研究学习,以期强化基础. 作者:Jaytalent.

捕获Java线程池执行任务抛出的异常

- - BlogJava-首页技术区
Java中线程执行的任务接口java.lang.Runnable 要求不抛出Checked异常,. 那么如果 run() 方法中抛出了RuntimeException,将会怎么处理了. 通常java.lang.Thread对象运行设置一个默认的异常处理方法:. 而这个默认的静态全局的异常捕获方法时输出堆栈.

Spring实现后台的任务调度TimerTask和Quartz

- - CSDN博客互联网推荐文章
最近整后台,涉及到两个后台调度的问题. 一是以时间间隔为条件的轮询调度;. 运用场景:每隔5分钟抓取数据;. 二是一某个时间点为条件的轮询调度;. 运用场景:后台日志货报表生成上传,每个周一生成上一周的,每个月初生成上一月. 其实按周来执行调度,用前面一个场景也可以实现,但是按月生成,因为每月时间不固定,必须动态判断和执行.

Spring 任务调度Quartz的cron表达式

- - ITeye博客
Spring支持基于Quartz的任务调度,那么其cron表达式类似于Linux的crontab,有7个字符构成,详情如下:. 表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五. 表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即等同于10,11,12.