2011-12-26 36 views
3

對不起,我必須打開一個新線程來描述此問題。無法停止使用ExecutorService啓動的任務

今天早上我問了this question,有一些答覆,但我的問題仍然沒有解決。

這一次,我會附加一些可運行的代碼(簡化,但同樣的問題)爲您重現該問題:

public class ThreadPoolTest { 
    public static void main(String[] args) throws Exception { 
     final ExecutorService taskExecutor = Executors.newFixedThreadPool(5); 
     Future<Void> futures[] = new Future[5]; 
     for (int i = 0; i < futures.length; ++i) 
      futures[i] = startTask(taskExecutor); 

     for (int i = 0; i < futures.length; ++i) 
      System.out.println("futures[i].cancel(true): " + futures[i].cancel(true)); 

     System.out.println("Cancel DONE."); 
     taskExecutor.shutdown(); 
    } 

    private static Future<Void> startTask(final ExecutorService taskExecutor) { 
     Future<Void> f = taskExecutor.submit(new Callable<Void>() { 
      public Void call() throws Exception { 
       try { 
        downloadFile(new URI("http://stackoverflow.com")); 
        while(true) { 
         System.out.println(Thread.currentThread().getName() + ": " + Thread.currentThread().isInterrupted()); 
         if(Thread.currentThread().isInterrupted()) 
          break; 
        } 
       } catch (Exception ex) { 
        ex.printStackTrace(); 
       } 
       return null; 
      } 
     }); 
     return f; 
    } 

    private static void downloadFile (final URI uri) throws Exception { 
//  if(true) return; 
     Socket socket = new Socket (uri.getHost(), uri.getPort() == -1 ? 80 : uri.getPort()); 
     return; 
    } 
} 

上面的代碼很有可能會陷入無限循環(您可能希望多次運行代碼以見證我所看到的),正如你可以在主要方法中看到的那樣,我爲所有任務調用futures [i] .cancel(true),我不知道爲什麼會發生這種情況,一直在折磨我一天以上。

您的幫助將不勝感激。

+0

您的問題可能與許多IO方法(網絡或磁盤)無法處理中斷有關。您可能需要在downloadFile中註釋掉套接字行並查看會發生什麼情況。此外,「陷入無限循環」的含義並不清楚:它是否會打印出線號或堵塞? – toto2 2011-12-26 15:40:20

+0

是的,如果我註釋掉套接字線,它按預期工作並且任務被正確取消。通過「陷入一個無限循環」我的意思是它不斷打印出線程名稱,這意味着Thread.currentThread()。isInterrupted()一直返回false。現在你已經指出了IO方法不能處理中斷的事實(我不知道),有沒有辦法在call()方法中攔截中斷,然後取消任務?現在套接字線似乎吞噬了我的中斷異常。 – neevek 2011-12-26 16:08:24

+0

我搜索了一下,看起來Socket的行爲沒有被定義爲中斷,而且一些不同的JVM有不同的行爲。 – toto2 2011-12-26 17:22:14

回答

5

我玩過你的代碼,並注意到線程的中斷狀態在套接字創建之前有時是真的,而在之後是false。

我試着打斷一個線程並調用Socket構造函數,並且線程始終保持中斷狀態。我也嘗試刪除線程池的關閉,並且問題繼續發生。

然後我試着使用5個不同的URI,而不是總是一樣的。問題從未發生過。

所以我寫了這個簡單的程序,顯示出線程池是不是罪魁禍首,但插座是:

public static void main(String[] args) throws Exception { 
    final URI uri = new URI("http://stackoverflow.com"); 
    for (int i = 0; i < 5; i++) { 
     Runnable r = new Runnable() { 
      @Override 
      public void run() { 
       Thread.currentThread().interrupt(); 
       System.out.println(Thread.currentThread().isInterrupted()); 
       try { 
        Socket socket = new Socket (uri.getHost(), uri.getPort() == -1 ? 80 : uri.getPort()); 
       } 
       catch (IOException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
       System.out.println(Thread.currentThread().isInterrupted()); 
      } 
     }; 
     new Thread(r).start(); 
    } 
} 

事實上,當5個線程創建一個套接字到同一主機和端口,4的中斷狀態被清除。

然後我試圖同步套接字創建(上一個鎖,但我猜你可能會使用每個主機/端口一個鎖):

synchronized(lock) { 
    try { 
     Socket socket = new Socket (uri.getHost(), uri.getPort() == -1 ? 80 : uri.getPort()); 
    } 
    catch (IOException e) { 
     // TODO Auto-generated catch block 
     e.printStackTrace(); 
    } 
} 

和多田......問題就消失了。我會在甲骨文公司發佈一個錯誤來表明問題。

+0

謝謝! JB Nizet,你救了我。很好發現它是一個錯誤。 – neevek 2011-12-26 17:08:59

0

我跑過你的代碼,並沒有停下來,就像你說的那樣。

沒有太多的時間去研究爲什麼它的行爲的話,但我發現,宣佈執行服務的線程作爲守護使問題消失:

private static ExecutorService TaskExecutor = Executors.newFixedThreadPool(5, new ThreadFactory() { 
    public Thread newThread(Runnable r) { 
     Thread t = new Thread(r); 
     t.setDaemon(true); 
     return t; 
    } 
}); 

我會回來,如果我找到更好的解釋。

+1

將線程設置爲守護進程會使其無法進入主線程,如果您在取消所有任務後執行了更多操作或在主線程中阻止該線程,則可能會注意到問題仍然存在。 – neevek 2011-12-26 16:28:22

+0

是的,製作過程「deamon」只是隱藏了問題。 – korifey 2011-12-26 16:56:22

0

我認爲當您嘗試取消它們時,任務未啓動的問題。我加了Thread.sleep(100)這樣的:

for (int i = 0; i < futures.length; ++i) 
     futures[i] = startTask(taskExecutor); 
Thread.sleep(100); 
for (int i = 0; i < futures.length; ++i) 
    System.out.println("futures[i].cancel(true): " + futures[i].cancel(true)); 

,一切都被取消OK。

+0

不,嘗試多次,你會發現它仍然掛起 – 2011-12-26 16:31:45

+0

由於未來的JavaDoc .cancel()表示:「試圖取消執行此任務。如果任務已完成,此嘗試將失敗,已經如果成功,並且這個任務在調用cancel時沒有開始,那麼這個任務不應該運行;如果任務已經啓動,那麼mayInterruptIfRunning參數確定線程是否執行這個任務應該中斷,試圖阻止任務。「即使任務未啓動,任務也可以取消。 – neevek 2011-12-26 16:38:30

+0

@OlivierCroisier嗯,也許我的機器性能更高。我試了15毫秒,它仍然有效,但暫停10毫秒。嘗試500毫秒 – korifey 2011-12-26 16:47:17