tomcat結合Java定時任務工具實現web中的定時任務
在Java實現簡單定時任務一篇記錄中描述的是Java的幾種定時任務的方式,那是最基本的,僅僅是main方法裡測試,用途也受限。
這裡研究一下在web中怎麼實現這些個定時任務,在這兒作個備忘:
在上一篇裡我們都是在Java執行的主執行緒中以main方法的方式來手動啟動和結束這些定時任務,這在web中怎麼實現呢?
首先,我們先找到這個定時任務啟動和結束的觸發事件;
然後,這個事件放生時就啟動我們的定時任務;
最後,檢查一下有沒有引入其他的陷阱(如果有,得解決)。
在web中有個contextListener,可以通過配置來加入到web應用中,這是個不錯的入口,廢話不多說,直接上菜吧:
context監聽器:
package abc; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; /** * * @author wz * API中描述:ServletContextListener繼承於EventListener,其實現者會在web應用的servlet context改變的時候收到事件通知, * 但是還必須要配置到web應用的部署檔案中(web.xml) */ public class MyContextListener implements ServletContextListener { private MyTimerTask3 mt3 = new MyTimerTask3(); /** * 本方法的描述: * 在所有的filter和servlet都destroyed後通知web應用的所有的ServletContextListeners[that“本店”即將打烊啦] */ public void contextDestroyed(ServletContextEvent sce) { System.out.println("contextDestroyed…………"); mt3.taskEnd(); } /** * 這個方法的描述: * 在所有的filter和servlet初始化之前,所有的ServletContextListeners會收到[您所在的web應用的初始化工作開始啦]通知 */ public void contextInitialized(ServletContextEvent arg0) { System.out.println("contextInitialized……"); mt3.taskBegin(); } }
我們的任務類:
package abc; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; /** * * @author wz jdk1.5提供了支援併發的定時任務處理工具ScheduledExecutorService * 1.以執行緒池的方式來執行任務,效率高了 * 2.可以設定開始延遲時間和任務取消時間 * */ public class MyTimerTask3 { public static void main(String[] args) { MyTimerTask3 mtt = new MyTimerTask3(); mtt.taskBegin(); mtt.taskEnd(); } // newScheduledThreadPool(int corePoolSize),corePoolSize是執行緒池的大小, //即保持活動或者閒置的執行緒,它們是任務排程時用於併發執行的執行緒 private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); ScheduledFuture<?> taskHandle; /** * 啟動任務 */ public void taskBegin() { // 定義一個任務 final Runnable task = new Runnable() { public void run() { System.out.println("執行任務"); } }; // scheduleAtFixedRate(Runnable command, long initialDelay, long period, // TimeUnit unit): // 通過ScheduledExecutorService的scheduleAtFixedRate來執行任務 // 引數 // command - 要執行的任務 // initialDelay - 首次執行的延遲時間 // period - 連續執行之間的週期 // unit - initialDelay 和 period 引數的時間單位 System.out.println("任務啟動………………………………"); taskHandle = scheduler.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS); } /** * 結束任務 */ public void taskEnd() { scheduler.shutdown(); //啟動一次順序關閉,執行以前提交的任務,但不接受新任務。如果已經關閉,則呼叫沒有其他作用。 // List<Runnable> shutdownNow()試圖停止所有正在執行的活動任務,暫停處理正在等待的任務,並返回等待執行的任務列表。 } }
web.xml新增的配置:
<listener>
<listener-class>abc.MyContextListener</listener-class>
</listener>
沒錯,就這樣就OK啦!
但是需注意:
在結束任務處
public void taskEnd() {
scheduler.shutdown(); //啟動一次順序關閉,執行以前提交的任務,但不接受新任務。如果已經關閉,則呼叫沒有其他作用。
// List<Runnable> shutdownNow()試圖停止所有正在執行的活動任務,暫停處理正在等待的任務,並返回等待執行的任務列表。
}
如果你是結束指定的任務,假設你不是在contextdestroy裡結束所有任務,而是結束指定的任務,其餘的任務照樣繼續執行,那你可以像下面這樣寫:
public void taskEnd() {
taskHandle.cancel(true);
}
其實,呼叫scheduler.shutdown();時,虛擬機器會去按順序(這種多定時任務中其實是維護著一個任務佇列的)調其taskHandle.cancel(true);
但是在你要停止所有任務時(就比如在整個web應用都要停止的時候),你不能只調taskHandle.cancel(true);
因為這樣的話,上建立的執行緒池中的執行緒還沒有關閉,會造成記憶體洩露一類的bug,比如它會告訴你[恭喜你,兄弟,你漏氣啦!]:
2015-5-24 1:11:56 org.apache.catalina.loader.WebappClassLoader clearReferencesThreads
嚴重: The web application [/abcsz_mng] appears to have started a thread named [pool-1-thread-1] but has failed to stop it. This is very likely to create a memory leak.
2015-5-24 1:11:57 org.apache.coyote.http11.Http11Protocol destroy