2011-07-29 52 views
2

我在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指點它...

  1. 延長ScheduledThreadPoolExecutor覆蓋 decorateTask()方法
  2. 裝點Runnable一個實施 ScheduledFuture接口
  3. 的實現一個自定義的cancel()方法,實際上 取消線程也操縱Runnable對象強制 資源釋放。

查看我的博客post瞭解詳情和代碼示例!

+0

你懷疑虛擬機退出嗎?而不是運行finally塊?如果是這樣,爲什麼不是在所有任務都被取消時退出虛擬機(即執行程序正在等待執行程序終止)?這可能只是一個競賽條件..? – Toby

+0

當tomcat應用程序關閉並且Lucene庫是服務器中的共享資源時會發生這種情況。因此,應用程序的所有線程都被取消了,但VM仍然活着,並且該庫仍可供其他Web應用程序使用。另一方面,當我們關閉應用程序時:'shutdown()',在'awaitTermination(幾分鐘)'後'shutdownNow()'。問題:Lucene索引重建是一個非常長的任務(甚至幾個小時),因此如果在調用'shutdownNow()'時執行索引重建,則線程會中斷,但資源不會釋放。 – ggarciao

+0

嗯,我會提出這是一個與lucene的錯誤!線程應該正確響應中斷IMO(他們應該實現正確的中斷策略)。你必須非常努力地解決這個問題...... – Toby

回答

0

你在安排什麼?這些任務是什麼樣子的?我覺得很難相信finally塊沒有執行。我猜想這是你已經安排好的任務,但還沒有開始執行,因爲資源泄漏(因爲它們的finally塊不會被執行)

聽起來像是CentOS上一個非常糟糕的VM實現,如果它真的沒有執行那些最後的塊。在任何其他VM實現中都沒有聽說過。

你可以做的一個選擇是代替ScheduledThreadPoolExecutor並覆蓋decorateTask方法,以便它們用你的類裝飾任務,然後攔截取消調用,而不是引用所有計劃的任務。

+0

我的期刊計劃任務更新Lucene索引(http://lucene.apache.org/java/docs/index.html)。 對於我們來說,finally塊的問題令人難以置信,但我們的診斷非常好:在我們的CentOS中,finally塊永遠不會到達(線程中斷後)。 感謝有關擴展池執行者的建議。我會檢查如何做到這一點。 – ggarciao

+0

@ggarciao - 你確定中斷髮生在finally塊內嗎?而且你沒有像Thread.stop()那樣做任何事情?正常的線程中斷應該_always_命中finally塊。另外,是否有可能你正在做一些對線程中斷敏感的finally塊,因此跳過finally塊的其餘部分? – jtahlborn

+0

並非如此,我正在關閉一些Lucene讀者,作者和目錄,而且這些操作也在其他try-catch塊之內。 – ggarciao