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

《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);
    }

}

你可能感兴趣的:(java)