我在JEE環境中獲得了用於任務調度的ScheduledExecutorService
。其中一些任務是在中斷ScheduledExecutorService.shutdownNow()
時打開資源(例如,使用Lucene等第三方庫打開文件)。我知道一個線程可能不會自行停止執行:必須使用的停止線程的方法是屏蔽中斷標誌並停止方法執行,並且如果線程阻塞(例如wait(),sleep() )等),或者如果在可中斷通道中執行一些IO操作,Thread.interrupt()
將使InterruptedException
上升。在這兩種情況下,都必須執行finally塊。 參見:http://download.oracle.com/javase/1,5.0/docs/api/java/lang/Thread.html#interrupt%28%29。顯然,我已經嘗試釋放資源,並在Task類中實現了一個很好實現的finally塊,但在某些環境下(例如CentOS),當線程中斷時,finally塊不會被執行。然後我發現了這個非常酷的音符在官方Java文檔:檢索計劃了ScheduledExecutorService的任務實例
注:如果JVM退出而被執行的嘗試或掛鉤代碼,然後 finally塊可以不執行。同樣,如果執行try或catch代碼的線程 被中斷或終止,則儘管應用程序整體 仍在繼續,但最終的 塊可能不會執行。
所以,我需要的是對所有計劃任務的引用,以便在強制釋放資源的任務類中實現一些公共方法。我可以從ScheduledExecutorService
中檢索對任務類的引用嗎?或者您是否有更好的方法來解決我的問題?
第一個解決方案:包裝它!
創建爲ScheduledExecutorService
的包裝類,並添加一個屬性是這樣的:
private IdentityHashMap<ScheduledFuture<?>, Runnable> taskList;
有了,我們可以直接訪問任何Runnable對象,或由ScheduledFuture
與之相關的。對於包裝器的實例化,我可以從Executors.newScheduledThreadPool()
方法獲得ScheduledExecutorService
並將它傳遞給我的包裝器。
另一種解決方案:擴展它!
擴展ScheduledThreadPoolExecutor
,添加IdentityHashMap屬性並覆蓋所有調度或取消作業的方法,以從Map添加/刪除引用。
這兩種解決方案的問題?
如果包裝或擴展類的調用者收到SchedulerFuture<?>
對象,則可以使用SchedulerFuture<?>.cancel()
方法取消該作業,繞過「膠囊」。使用包裝器,您可以避免將參考傳遞給調用者,但是對於擴展類您不能(如果您在擴展類中創建自己的方法,您將獲得與包裝器相同的結果,但是以非常混淆的方式)。
優雅的解決方案:您自己的調度程序!感謝Kaj指點它...
- 延長
ScheduledThreadPoolExecutor
覆蓋decorateTask()
方法 - 裝點
Runnable
一個實施ScheduledFuture
接口 - 的實現一個自定義的
cancel()
方法,實際上 取消線程也操縱Runnable
對象強制 資源釋放。
查看我的博客post瞭解詳情和代碼示例!
你懷疑虛擬機退出嗎?而不是運行finally塊?如果是這樣,爲什麼不是在所有任務都被取消時退出虛擬機(即執行程序正在等待執行程序終止)?這可能只是一個競賽條件..? – Toby
當tomcat應用程序關閉並且Lucene庫是服務器中的共享資源時會發生這種情況。因此,應用程序的所有線程都被取消了,但VM仍然活着,並且該庫仍可供其他Web應用程序使用。另一方面,當我們關閉應用程序時:'shutdown()',在'awaitTermination(幾分鐘)'後'shutdownNow()'。問題:Lucene索引重建是一個非常長的任務(甚至幾個小時),因此如果在調用'shutdownNow()'時執行索引重建,則線程會中斷,但資源不會釋放。 – ggarciao
嗯,我會提出這是一個與lucene的錯誤!線程應該正確響應中斷IMO(他們應該實現正確的中斷策略)。你必須非常努力地解決這個問題...... – Toby