2015-04-26 59 views
3

我有一個系統,它在接收到來自web服務的調用時啓動worker。工作人員由ExecutorService啓動,啓動的類實現Runnable。但是,如果員工超時,我無法真正殺死工人,這導致我的系統出現資源問題。Java:通過ExecutorService殺死作爲Runnable啓動的線程

public class MyClass implements Runnable { 

    public void internalCall() { 
     logger.info("B-1"); 
     //Some business code which may take too long 
     // <...> 
     logger.info("B-2"); 
    } 

    public void launch() { 
     // Wrapper 
     Callable<Object> callable = new Callable<Object>() { 
      @Override 
      public Object call() throws Exception { 
       internalCall(); 
       return null; 
      } 
     }; 

     // Submit 
     ExecutorService executor = Executors.newSingleThreadExecutor(); 
     Future<Object> future = executor.submit(callable); 

     try { 
      // Wait 
      future.get(1, TimeUnit.SECONDS); 
     } 
     catch (TimeoutException e) { 
      logger.warn("Timeout"); 
     } 
     finally { 
      logger.info("A-1"); 
      executor.shutdownNow(); 
      future.cancel(true); 
      logger.info("A-2"); 
     } 
    } 
} 

如果工作超時,我希望下面的日誌消息:

INFO | B-1 
WARN | Timeout 
INFO | A-1 
INFO | A-2 

其次是服務剩餘空閒直到另一名工人請求到來然而,儘管調用shutdownNow時()和取消。 ()上的ExecutorService和未來體面,工作人員繼續說:

INFO | B-1 
WARN | Timeout 
INFO | A-1 
INFO | A-2 
INFO | B-2 

我環顧四周,並有許多其他類似的問題,關於殺害threa ds,普遍認爲你不應該這樣做。然而,這是一個可以擴展的類,意圖覆蓋internalCall() - 這意味着我不能依靠internalCall來自我檢查並檢查Thread.isInterrupted()或類似的東西。

我想通過攻擊furure或executor對象來強制從launch()方法中殺死東西。

+0

工作線程由執行程序服務維護 - 它們只是由提交的任務「借用」,通常用於許多任務執行。你不允許簡單地「殺死」它們! – isnot2bad

+0

那麼爲什麼不停止執行程序服務停止線程呢? – tim

+0

ExecutorService嘗試停止這些線程,但如果您有錯誤的代碼,則不能保證它會發生。詳情請參閱我的回答。 –

回答

1

首先,future.cancel(true)不殺死正在運行的線程。它只是試圖以「禮貌的態度」停止執行。

它做什麼它發送中斷信號,或多或少,相同的方式,如果你在你的代碼中的某處稱爲yourthread.interrupt()

只有當run()方法中的代碼檢查中斷時,它纔會停止處理。因此,在您的internalCall()中,您需要不時檢查線程是否因調用Thread.interrupted()而中斷並停止執行。中斷還會通過拋出InterruptedExcepiton來停止sleep(),wait(),IO操作(這就是爲什麼這些方法拋出這種異常)。

ExecutorService正在使用相同的機制,因此它會嘗試中斷正在運行的線程,但是,如果您的代碼不檢查這種中斷,線程將繼續運行。

由於各種原因,不應該簡單地殺死線程(檢查java doc for thread.stop()方法以及爲什麼它被棄用),並且通知線程可能應該停止工作的「通用」方式是中斷信號。

+0

這不是一個可行的答案。通過ExecutorService提交的元素將包含由第三方編寫的代碼,因此我們不能保證internalCall將正確檢查Thread.currentThread()。isInterrupted()(如原始問題中概述的)。應該有辦法強行阻止工人而不依賴工人的優雅 - 當問題出現時是錯誤的情況下,優雅的處理是沒有意義的。 – tim

+0

不要使用執行程序服務,然後創建自己的線程並對它們停止呼叫。你問到未來和執行者,這就是他們提供的一切 – Zielu

3

N.B.

如果線程不響應Thread.interrupt會怎麼樣?

在某些情況下,您可以使用特定於應用程序的技巧。例如, 如果某個線程正在等待已知的套接字,則可以關閉套接字至 ,導致該線程立即返回。不幸的是,真的沒有任何一般工作的技術。 應該注意的是,在 所有情況下,等待線程不響應Thread.interrupt,它也不會響應Thread.stop。此類 個案包括故意拒絕服務攻擊和其中線程的I/O操作 。停止和thread.interrupt不能正常工作。 - Java Thread Primitive Deprecation

所以,我們學到了什麼...在Java不運行的第三方代碼,你真的不能在同一個執行作爲主應用程序信任。即使Thread.stop沒有工作,你仍然有很多其他事情比沒有檢查中斷狀態的線程差得多(例如代碼調用System.exit(0))。

我建議你什麼第三方代碼,你不能信任或者是做:

  • 運行第三方代碼被Java,你控制的執行運行評估語言。一些例子是:像Drools這樣的規則語言或像JMustache這樣的無邏輯模板語言。

  • 在單獨的執行中運行第三方代碼,並使用您的操作系統來終止進程和IPC,如套接字進行通信。