2016-03-07 505 views
2

我有使用ExecutorsJava線程池,如何使用shutdownNow()立即停止長時間運行的線程?

ExecutorService executor = Executors.newFixedThreadPool(4); 

每個線程都有長時間運行的作業(從其他球隊的一些遺留代碼),這可能會運行幾個小時創建多個線程的主線程。

現在我想使用

executor.shutdownNow() 

主線程關閉和我想要的線程能夠立即停止,我怎麼能做到這一點?

在線程,說我們有這樣的代碼:

public void run() { 
doA(); 
doB(); 
doC(); 
... 
... 

}

現在我的問題是,即使我叫shutdownNow,正在運行的線程將運行到結束,然後停止。我想知道如何停止並退出。

+0

只是想知道爲什麼你使用一個線程的順序任務?可能需要重新設計一下,在你的線程中使用Callables而不是Runnables,你可以嘗試在shutdownNow()調用時調用someTask.cancel()來清除正在運行的任務。 –

+0

這是一個設計問題。我會要求時間窗口稍後再做。對於這個問題,我只想確認在Java中沒有什麼好方法可以像短路一樣停止運行線程。 – Ran

+0

@Ran我的回答是否滿足你的約束? –

回答

1

這的確有點棘手!

我們可以利用JDK以ThreadFactory的形式提供的鉤子,當關聯的線程池正在創建一個將在其中運行遺留任務的線程時,該鉤子將被查詢。如果是,那麼爲什麼不讓你的遺留代碼在daemon線程中運行?我們知道當最後一個non-daemon線程退出時,JVM會退出。因此,如果我們作出這樣的線程池用於運行遺留任務的守護線程每個線程有機會的話,我們可以讓shutdownNow()通話更加敏感:

public class LegacyCodeExecutorEx { 
    public static void main(String[] args) throws InterruptedException { 

     ExecutorService executor = Executors.newFixedThreadPool(2, new DaemonThreadFactory()); 

     executor.submit(new LegacySimulator()); 
     Thread.sleep(1000); 
     executor.shutdownNow(); 
    } 

    static class LegacySimulator implements Runnable { 
     private final AtomicLong theLong; 
     LegacySimulator() { 
      theLong = new AtomicLong(1); 
     } 
     @Override 
     public void run() { 
      for (long i = 10; i < Long.MAX_VALUE; i++) { 
       theLong.set(i*i); 
      } 
      System.out.println("Done!"); 
     } 
    } 
    static class DaemonThreadFactory implements ThreadFactory { 
     @Override 
     public Thread newThread(Runnable r) { 
      Thread t = new Thread(r); 
      t.setName("Daemon Thread"); 
      t.setDaemon(true); 
      return t; 
     } 
    } 
} 

如果你setDaemon(true)線玩,你會看到這段代碼要麼立即響應主線程(即non-daemon)的退出,要麼花費自己的甜蜜時間來完成任務。

讓您的遺留代碼運行線程daemon線程有可能嗎?如果是的話,你可以試試這個。

+0

是的,守護進程現在解決了大部分問題。我必須首先保留他們的遺留代碼。現在是時候分手了。你不會相信他們在那裏有多少重複的代碼........但是非常感謝答案 – Ran

0

您需要在Runnable對象實例化中包含一個標誌,用於在任務之間檢查是否需要停止。

public void run() { 
    if(timeToShutdown) return; 
    doA(); 
    if(timeToShutdown) return; 
    doB(); 
    /*etc*/ 
} 

線程在Java中在(相對)低的水平操作。沒有直接關閉整個JVM,手動強制停止線程的唯一方法是使用來自Java 1.0/1.1的棄用行爲,幾乎沒有人希望您使用它。

+0

感謝您的回覆。這是我想要做的最後一種方式,我也有類似的方法,就像你提到的,但我使用if(Thread.interrupted())...事情是每個doA(),doB()都會調用很多東西,每個都可能是長時間運行(主要是I/O),如果我無法正確阻止它們,我們的PM會重新運行這個工作,然後我可能有一些重複的線程在同一件事情上工作,這是一開始真的是一個糟糕的設計...... – Ran

+0

@Ran如果你需要任務本身來結束在一刻的注意,那麼你需要添加類似的邏輯,好。如果這是不可能的,那麼你確實在處理代碼中的一個相當嚴重的設計問題。 – Xirema

+0

嘆息....是的......這是一個來自很多人的傳統代碼給我。他們管理一個線程,在這個線程中處理數百萬行文本文件。將有sanitycheck,重複數據刪除,獨特的id,計算等等,很多東西都集成到一個函數中。我不喜歡這個,但重構它們太大了。現在他們遇到了一些大問題,如果出現問題,他們會重新運行,重新運行會帶來更多問題。根本原因是這種關閉並沒有真正關閉正在運行的線程,所以新的線程將在同一個文件上與舊的線程一起工作,搞砸了一切................... – Ran