2010-07-26 78 views
32

以下代碼片段試圖完成此操作。線程和執行程序的優雅關閉

該代碼將永久循環並檢查是否有任何待處理的請求需要處理。如果有的話,它會創建一個新線程來處理請求並將其提交給執行者。一旦所有線程完成,它會休眠60秒,並再次檢查未決請求。

public static void main(String a[]){ 
    //variables init code omitted 
    ExecutorService service = Executors.newFixedThreadPool(15); 
    ExecutorCompletionService<Long> comp = new ExecutorCompletionService<Long>(service); 
    while(true){ 
     List<AppRequest> pending = service.findPendingRequests(); 
     int noPending = pending.size(); 
     if (noPending > 0) { 
      for (AppRequest req : pending) { 
       Callable<Long> worker = new RequestThread(something, req); 
       comp.submit(worker); 
      } 
     } 
     for (int i = 0; i < noPending; i++) { 
      try { 
       Future<Long> f = comp.take(); 
       long name; 
       try { 
        name = f.get(); 
        LOGGER.debug(name + " got completed"); 
       } catch (ExecutionException e) { 
        LOGGER.error(e.toString()); 
       } 
      } catch (InterruptedException e) { 
       LOGGER.error(e.toString()); 
      } 
     } 
     TimeUnit.SECONDS.sleep(60); 
    } 

    } 

我的問題是這些線程處理數據庫的大部分處理。這個程序將運行在Windows機器上。當有人試圖關閉或註銷機器時,這些線程會發生什麼?如何正常關閉正在運行的線程以及執行程序。

回答

55

一個ExecutorService的典型有序地關閉可能是這個樣子:

final ExecutorService executor; 

Runtime.getRuntime().addShutdownHook(new Thread() { 
    public void run() { 
     executor.shutdown(); 
     if (!executor.awaitTermination(SHUTDOWN_TIME)) { //optional * 
      Logger.log("Executor did not terminate in the specified time."); //optional * 
      List<Runnable> droppedTasks = executor.shutdownNow(); //optional ** 
      Logger.log("Executor was abruptly shut down. " + droppedTasks.size() + " tasks will not be executed."); //optional ** 
     } 
    } 
}); 

*您可以登錄執行者在等待你願意等待的時間後仍然有任務要處理。
**您可以嘗試強制執行程序的工作線程放棄當前的任務,並確保它們不會啓動任何剩餘的一些。

請注意,當用戶向java進程發出中斷或者ExecutorService僅包含守護進程線程時,上述解決方案將工作。相反,如果在ExecutorService包含未完成非守護線程時,JVM不會嘗試關閉,因此關閉掛鉤將不會被調用。

如果試圖關機的處理作爲分立應用程序生命週期(不是服務),那麼關閉代碼​​不應該被放置在關閉鉤內的一部分,但是在該程序被設計成終止適當的位置。

+0

這是倒退。假設'ExecutorService'是由'Executors'工廠方法返回的典型值之一,背襯線程非守護線程。在這些線程退出之前,關閉鉤子將不會被調用,並且直到「ExecutorService」關閉纔會發生。只有在執行程序的線程不是守護進程或者程序收到用戶中斷的情況下,您的解決方案纔會生效。 – 2015-10-07 01:11:17

+0

的OP大約是在JVM已經被要求執行有序關機的情況下專門詢問。無論如何,答案的內容都是準確的,只需要在關閉鉤子中不要這樣做就可以提前終止執行程序服務。 – 2015-10-07 01:22:21

+0

非常感謝您的回答。我想我錯過了他們問題的那一部分。你可以添加第三個***來澄清這一點嗎? – 2015-10-07 01:24:28

7

書 「Java Concurrency in Practice」 規定:

7.4。 JVM關機

JVM可以以有序或突然的方式以 關閉。當最後一個 「正常」(nondaemon)線程 終止,有人調用System.exit, 或其他特定於平臺的方式 (如發送SIGINT或打 按Ctrl-C)的有序 關機啓動。 [...]

7.4.1。 Shutdown Hooks

在有序關閉中,JVM第一個 啓動所有已註冊的關閉掛鉤。 關閉掛鉤未登記的線程 已註冊 Runtime.addShutdownHook。 JVM對 關閉掛鉤的啓動順序無法保證 。如果任何 應用程序線程(守護進程或 非守護進程)仍在關閉時間 上運行,則它們將繼續與關閉 進程同時運行 。當所有關閉掛鉤已完成 時,如果runFinalizersOnExit爲 爲true,則JVM可能選擇運行 終結器,然後暫停。 JVM不會讓 嘗試停止或中斷仍在關機時運行的 的任何 應用程序線程;當JVM 最終停止時,它們是 突然終止。如果關閉 掛鉤或終結器未完成,則 然後有序關閉進程 「掛起」,並且JVM必須突然關閉 。 [...]

重要的位, 「的JVM沒有試圖阻止或中斷仍在關機時運行的任何應用程序線程;當JVM最終停止,他們會突然終止」所以我想連接數據庫會突然終止,如果沒有關閉鉤子在那裏做一個優雅的清理(如果你使用的是框架,他們通常會提供這樣的關閉鉤子)。根據我的經驗,到數據庫的會話可以保留,直到數據庫超時,等應用程序。沒有這樣的鉤子而被終止。

2

您可以撥打shutdown()ExecutorService

發起一個有序的關閉,其中 以前提交的任務執行 ,但沒有新的任務將 接受。

,或者您可以撥打shutdownNow()

嘗試停止所有正在 執行任務,暫停等待任務的處理 ,並返回一個列表的那個正在等待 執行的任務 。

除 之外沒有任何保證盡力嘗試阻止 處理主動執行任務。 例如,典型的實現方式中 將通過了Thread.interrupt()取消,所以 失敗向 響應中斷可能永遠不會終止任何任務。

調用哪一個取決於你想有多嚴重就停止....

+0

我在我的web應用程序中遇到以下問題。如何停止內存泄漏.http://stackoverflow.com/questions/39166512/memory-leak-error-in-tomcat-server-even-after-removed-quartz相關代碼 – Vicky 2016-08-27 05:48:43

5

由於添加關機鉤以明確調用shutdown()對我無效,所以我在Google的Guava中找到了一個簡單的解決方案: com.google.common.util.concurrent.MoreExecutors.getExitingExecutorService

+1

不過,它有點問題,因爲'MoreExecutors.getExitingExecutorService'只接受'ThreadPoolExecutor'實例。因此,您必須手動定義這些執行程序,而不是簡單地調用,例如返回'ExecutorService'的'Executors.newFixedThreadPool'。 – voo 2013-07-12 14:53:11

相關問題