我們知道靜態(tài)變量是ClassLoader級別的,如果Web應(yīng)用程序停止,這些靜態(tài)變量也會從JVM中清除。但是線程則是JVM級別的,如果你在Web 應(yīng)用中啟動一個線程,這個線程的生命周期并不會和Web應(yīng)用程序保持同步。也就是說,即使你停止了Web應(yīng)用,這個線程依舊是活躍的。正是因?yàn)檫@個很隱晦 的問題,所以很多有經(jīng)驗(yàn)的開發(fā)者不太贊成在Web應(yīng)用中私自啟動線程。
如果我們手工使用JDK Timer(Quartz的Scheduler),在Web容器啟動時啟動Timer,當(dāng)Web容器關(guān)閉時,除非你手工關(guān)閉這個Timer,否則Timer中的任務(wù)還會繼續(xù)運(yùn)行! 下面通過一個小例子來演示這個“詭異”的現(xiàn)象,我們通過ServletContextListener在Web容器啟動時創(chuàng)建一個Timer并周期性地運(yùn)行一個任務(wù):
在web.xml中聲明這個Web容器監(jiān)聽器:<?xml version="1.0" encoding="UTF-8"?> <web-app> … <listener> <listener-class>com.baobaotao.web.StartCycleRunTask</listener-class> </listener> </web-app> 在Tomcat中部署這個Web應(yīng)用并啟動后,你將看到任務(wù)每隔5秒鐘執(zhí)行一次。 運(yùn)行一段時間后,登錄Tomcat管理后臺,將對應(yīng)的Web應(yīng)用(chapter13)關(guān)閉。 轉(zhuǎn)到Tomcat控制臺,你將看到雖然Web應(yīng)用已經(jīng)關(guān)閉,但Timer任務(wù)還在我行我素地執(zhí)行如故——舞臺已經(jīng)拆除,戲子繼續(xù)表演: 我們可以通過改變清單StartCycleRunTask的代碼,在contextDestroyed(ServletContextEvent arg0)中添加timer.cancel()代碼,在Web容器關(guān)閉后手工停止Timer來結(jié)束任務(wù)。 Spring為JDK Timer和Quartz Scheduler所提供的TimerFactoryBean和SchedulerFactoryBean能夠和Spring容器的生命周期關(guān)聯(lián),在 Spring容器啟動時啟動調(diào)度器,而在Spring容器關(guān)閉時,停止調(diào)度器。所以在Spring中通過這兩個FactoryBean配置調(diào)度器,再從 Spring IoC中獲取調(diào)度器引用進(jìn)行任務(wù)調(diào)度將不會出現(xiàn)這種Web容器關(guān)閉而任務(wù)依然運(yùn)行的問題。而如果你在程序中直接使用Timer或Scheduler,如不 進(jìn)行額外的處理,將會出現(xiàn)這一問題。 |
|