Java 定時任務系列(1)- Java原生支持

一、普通thread實現java

這是最多見的,建立一個thread,而後讓它在while循環裏一直運行着,經過sleep方法來達到定時任務的效果。這樣能夠快速簡單的實現,代碼以下:web

public class Task1 {
public static void main(String[] args) {
  // run in a second
  final long timeInterval = 1000;
  Runnable runnable = new Runnable() {
  public void run() {
    while (true) {
      // ------- code for task to run
      System.out.println("Hello !!");
      // ------- ends here
      try {
       Thread.sleep(timeInterval);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      }
    }
  };
  Thread thread = new Thread(runnable);
  thread.start();
  }
}

二、用Timer和TimerTask安全

介紹
java.util.Timer定時器,其實是個線程,定時調度所擁有的TimerTasks。
一個java.util.TimerTask實際上就是一個擁有run方法的類,須要定時執行的代碼放到run方法體內,TimerTask通常是以匿名類的方式建立。
上面的實現是很是快速簡便的,但它也缺乏一些功能。
用Timer和TimerTask的話與上述方法相比有以下好處:
當啓動和去取消任務時能夠控制
第一次執行任務時能夠指定你想要的delay時間
在實現時,Timer類能夠調度任務,TimerTask則是經過在run()方法裏實現具體任務。
Timer實例能夠調度多任務,它是線程安全的。
當Timer的構造器被調用時,它建立了一個線程,這個線程能夠用來調度任務。併發

下面是代碼:
Timer 測試類代碼:app

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

//import com.baibutao.apps.linkshop.uxiang.server.util.DateUtil;


public class TestTimerTask {

        public static void main(String[] args){
            TaskA taskA = new TaskA();
            Timer timer = new Timer();
            timer.schedule(taskA, 5 * 1000, 5 * 1000);

            //Date date = DateUtil.parse("2014-12-04 16:50:00");
            //timer.schedule(taskA, date , 5 * 1000);
            //timer.scheduleAtFixedRate(taskA, date, 5 * 1000);
        }
    }

任務類代碼:ide

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

import wint.lang.utils.DateUtil;

// 定義一個任務類
public class TaskA extends TimerTask{

    @Override
    public void run() {
        System.out.println(DateUtil.formatFullDate(new Date()));
    }

}

關於Timer類的調用方法:
void java.util.Timer.schedule(TimerTask task, long delay):多長時間(毫秒)後執行任務工具

void java.util.Timer.schedule(TimerTask task, Date time):設定某個時間執行任務測試

void java.util.Timer.schedule(TimerTask task, long delay, long period):delay時間後開始執行任務,並每隔period時間調用任務一次。this

void java.util.Timer.schedule(TimerTask task, Date firstTime, long period):第一次在指定firstTime時間點執行任務,以後每隔period時間調用任務一次。線程

void java.util.Timer.scheduleAtFixedRate(TimerTask task, long delay, long period):delay時間後開始執行任務,並每隔period時間調用任務一次。

void java.util.Timer.scheduleAtFixedRate(TimerTask task, long delay, long period):第一次在指定firstTime時間點執行任務,以後每隔period時間調用任務一次。

void java.util.Timer.cancel():終止該Timer

boolean java.util.TimerTask.cancel():終止該TimerTask

能夠爲每一個Timer指定多個TimerTask

雖然可經過void java.util.Timer.schedule(TimerTask task, Date firstTime, long period)方法完成「例如:天天上午10點執行一次」的業務,但該實現是基於進行一天(1000 * 60 * 60 * 24毫秒)進行延遲的機制實現的,並非指定某個具體時間進行執行的。

對於該種需求,可經過Quartz來進行實現

方法名稱schedule()和scheduleAtFixedRate()二者的區別
當須要根據period區間時間循環屢次調用任務的時候,會存在兩種不一樣的策略,兩種方法提供了不一樣的策略。
調用方法(1)、(2)只是單次執行,不存在屢次調用任務的狀況,因此沒有提供scheduleAtFixedRate方法的調用方式。
schedule()方法更注重保持間隔時間的穩定:保障每隔period時間可調用一次
scheduleAtFixedRate()方法更注重保持執行頻率的穩定:保障屢次調用的頻率趨近於period時間,若是某一次調用時間大於period,下一次就會盡可能小於period,以保障頻率接近於period
進一步的說明參見eg366的博客,星星的技術專欄

三、ScheduledExecutorService

ScheduledExecutorService是從Java SE 5的java.util.concurrent裏,作爲併發工具類被引進的,這是最理想的定時任務實現方式。
相比於上兩個方法,它有如下好處:

相比於Timer的單線程,它是經過線程池的方式來執行任務的
能夠很靈活的去設定第一次執行任務delay時間
提供了良好的約定,以便設定執行的時間間隔

下面是實現代碼,咱們經過ScheduledExecutorService#scheduleAtFixedRate展現這個例子,經過代碼裏參數的控制,首次執行加了delay時間。

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class Task3 {
  public static void main(String[] args) {
    Runnable runnable = new Runnable() {
      public void run() {
        // task to run goes here
        System.out.println("Hello !!");
      }
    };
    ScheduledExecutorService service = Executors
                    .newSingleThreadScheduledExecutor();
    service.scheduleAtFixedRate(runnable, 0, 1, TimeUnit.SECONDS);
  }
}

關於ScheduledExecutorService 和 Timer的優劣,Timer沒有很好的錯誤處理機制,請參考苦行僧的博客
關於ScheduledExecutorService 的具體用法,請參照濤濤的博客

四、web容器中利用ServletContextListener實現定時任務

一個實現ServletContextListener接口的類: StatisticsContextListener.java:

package com.ed.cnc.servletListener;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import com.ed.cnc.city.StatisticsTask;

/**
 * 統計ContextListener
 * @author westd
 *
 */
/**
 * @author westd
 *
 */
public class StatisticsContextListener implements ServletContextListener
{

    private java.util.Timer timer = null;


    /**
     * 這個方法在Web應用服務作好接受請求的時候被調用。
     * 
     * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent)
     */
    public void contextInitialized(ServletContextEvent event) 
    {
        timer = new java.util.Timer(true);
        event.getServletContext().log("定時器已啓動"); 
        timer.schedule(new StatisticsTask(event.getServletContext()), 0, 60*60*1000);//每隔1小時
        event.getServletContext().log("已經添加任務調度表");
    }


    /**
     * 這個方法在Web應用服務被移除,沒有能力再接受請求的時候被調用。
     * 
     * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent)
     */
    public void contextDestroyed(ServletContextEvent event)
    {
        timer.cancel();
        event.getServletContext().log("定時器銷燬");
    }

}

一個繼承於TimerTask的一個類:StatisticsTask.java

package com.ed.cnc.city;

import java.util.Calendar;
import java.util.TimerTask;

import javax.servlet.ServletContext;


/**
 * 統計任務
 * @author westd
 *
 */
public class StatisticsTask extends TimerTask
{

    private static final int STATISTICS_SCHEDULE_HOUR = 0;
    private static boolean isRunning = false;
    private ServletContext context = null;

    public StatisticsTask(ServletContext context)
    {
        this.context = context;
    }

    @Override
    public void run()
    {
        Calendar cal = Calendar.getInstance(); 
        //System.out.println(isRunning);
        if (!isRunning) 
        { 
            if (STATISTICS_SCHEDULE_HOUR == cal.get(Calendar.HOUR_OF_DAY)) //查看是否爲凌晨
            { 
                isRunning = true; 
                context.log("開始執行指定任務");

                //TODO 添加自定義的詳細任務
                executeTask();

                //指定任務執行結束
                isRunning = false;
                context.log("指定任務執行結束"); 
            } 
        } 
        else 
        {
            context.log("上一次任務執行還未結束");
        }

    }
/**
 * 執行任務
 */
public void executeTask()
{
    System.out.println("任務1");
    System.out.println("任務2");
}

}
web.xml中添加以下代碼:

<listener>
        <listener-class>com.ed.cnc.servletListener.StatisticsContextListener</listener-class>
</listener>
相關文章
相關標籤/搜索