2010-09-23 161 views
6

我在寫一個產生多個併發任務的應用程序。我正在使用線程池來實現它。如何停止線程池中的線程

可能發生的事件發生使得任務中的計算無效。在這種情況下,我想停止當前正在運行的任務,並開始新的任務。

我的問題:如何停止當前正在運行的任務?我實現的解決方案是存儲對任務線程的引用,並在此線程上調用interrupt()。在演示代碼:

public class Task implements Runnable { 

    private String name; 
    private Thread runThread; 

    public Task(String name) { 
     super(); 
     this.name = name; 
    } 

    @Override 
    public void run() { 
     runThread = Thread.currentThread(); 

     System.out.println("Starting thread " + name); 
     while (true) { 
      try { 
       Thread.sleep(4000); 
       System.out.println("Hello from thread " + name); 
      } catch (InterruptedException e) { 
       // We've been interrupted: no more messages. 
       return; 
      } 
     } 
    } 

    public void stop() { 
     runThread.interrupt(); 
    } 

    public String getName() { 
     return name; 
    } 
} 

和主要方法是:

public static void main(String args[]) { 
    executorService = Executors.newFixedThreadPool(2); 

    Task t1 = new Task("Task1"); 
    Task t2 = new Task("Task2"); 
    executorService.execute(t1); 
    executorService.execute(t2); 
    executorService.execute(new Task("Task3")); 
    executorService.execute(new Task("Task4")); 

    try { 
     Thread.sleep(12000); 
     t1.stop(); 
     System.err.println("Stopped thread " + t1.getName()); 
     Thread.sleep(8000); 
     t2.stop(); 
     System.err.println("Stopped thread " + t2.getName()); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
} 

這是一個很好的解決方案,或者有更好的辦法阻止在一個線程池正在運行的線程?

+1

拔插頭;) – sje397 2010-09-23 11:27:20

回答

2

在您重寫的run()方法中,您將永遠循環while(true)。標準行爲應該是boolean runIndicator,其中run()方法在啓動時設置爲true,然後您的循環應爲while(runIndicator)。您的stop()方法應該簡單設置runIndicator = false,因此循環的下一次迭代將會失敗。

+0

在這個實現中,'stop()'方法也應該調用'this.interrupt()'。在實際的實現中,應該有一個類似的方法,您可以使用它來停止正在進行的繁重工作,而不是「Thread.sleep *(4000)'調用。你可以使用一個非常類似於'runIndicator'的方法來實現這一點。 – 2010-09-23 11:34:20

+0

@Erick,'interrupt()'不是可運行的方法,所以這是不可能的。 Thread.currentThread().interrupt()'不會工作,因爲stop()的調用發生在另一個線程而不是你想要中斷的線程中。 – Thirler 2010-09-23 11:39:37

+1

對不起,我沒有看到這個。我總是擴展'Thread'而不是'Runnable',所以我總是可以訪問這些東西。我會在這裏做同樣的事情。這也可以解決您在答案中發佈的微小線程安全問題。 – 2010-09-23 11:41:32

3

您的方法背後的想法是幾個正確的解決方案之一。 Dealing with InterruptedException對如何使用中斷機制給出了一個很好的概述。當你長時間計算時,這個機制主要是有用的。還有一點需要注意的是,其他庫可能會通過不按照指南的說明來破壞您的中斷機制(不會在未處理它時重置中斷狀態等)。

請注意,您的Task類不是線程安全的。您可以在保存currentThread之前停止該任務,這會產生NullPointerException。

一個更簡單的方法是設置和的volatile boolean可變running代替while(true)循環做while(running)方法(這是但更普遍)。

另一個需要考慮的問題是FutureTask機制,因爲它已經有一個使用中斷機制的取消機制。

1

應該使用executorService.shutdown()和executorService.shutdownNow()來關閉線程池以正常退出應用程序。見ExecutorService

請參閱Qwerky關於結束當前正在運行的線程的回答。

+4

他想關閉一個線程,而不是整個池。 – 2010-09-23 11:40:32

+0

答案由Qwerky給出。但是還需要關閉線程池才能正常退出應用程序。 – 2010-09-23 12:02:22

2

您可以拿着一個參考未來

  Future<?> future = exec.submit(new Runnable() { 
     while (true){ 
     try{ 
      obj.wait(); 
     }catch(InterruptedException e){ 
      System.out.println("interrupted"); 
      return; 
     } 
     }); 
     future.cancel(true); 

布爾停止它是 - 如果運行可能會中斷。

我測試了出來,並從該線程得到一箇中斷的異常。

如果你有cachedThreadPool,你可能需要仔細檢查你是否捕獲到可運行的異常,然後不要設置中斷標誌,因爲你的線程將運行另一個未來,如果你設置中斷,另一個隊列未來可能無法運行。