一、普通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>