1. 程式人生 > >tomcat結合Java定時任務工具實現web中的定時任務

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