2009-09-13 31 views
9

我已經使用執行程序提交了一個任務,並且需要它在一段時間後停止(例如5分鐘)。我試圖做這樣的:Java執行者:如何停止提交的任務?

for (Future<?> fut : e.invokeAll(tasks, 300, TimeUnit.SECONDS)) { 
     try { 
      fut.get(); 
     } catch (CancellationException ex) { 
      fut.cancel(true); 
      tasks.clear(); 
     } catch(ExecutionException ex){ 
      ex.printStackTrace(); //FIXME: gestita con printstack  
     } 
    } 

但我總是得到一個錯誤:我有一個共同的載體,需要由任務由一個線程修改,然後閱讀,即使我停止所有的任務,如果發生超時,我得到:

Exception in thread "Thread-1" java.util.ConcurrentModificationException 

有什麼錯嗎?如何停止5分鐘後仍在工作的任務?

+0

@Raffaele迪法齊奧:我已格式化的代碼 - 並增加了一個右括號,請檢查的準確性。 – akf 2009-09-13 16:18:16

+0

謝謝,我很抱歉格式不正確。 – Raffo 2009-09-13 17:02:45

回答

20

僅因爲您在Future上撥打cancel()並不意味着該任務將自動停止。你必須在任務中做一些工作,以確保它能夠停止:

  • 使用cancel(true),以便中斷髮送到任務。
  • 手柄InterruptedException。如果任務中的函數引發InterruptedException,請確保在捕獲異常時儘快正常退出。
  • 定期檢查Thread.currentThread().isInterrupted()如果任務執行連續計算。

例如:

class LongTask implements Callable<Double> { 
    public Double call() { 

     // Sleep for a while; handle InterruptedException appropriately 
     try { 
      Thread.sleep(10000); 
     } catch (InterruptedException ex) { 
      System.out.println("Exiting gracefully!"); 
      return null; 
     } 


     // Compute for a while; check Thread.isInterrupted() periodically 
     double sum = 0.0; 
     for (long i = 0; i < 10000000; i++) { 
      sum += 10.0 
      if (Thread.currentThread().isInterrupted()) { 
       System.out.println("Exiting gracefully"); 
       return null; 
      } 
     } 

     return sum; 
    } 
} 

而且,其他職位都提到:ConcurrentModificationException可即使使用線程安全Vector類拋出,因爲迭代器從Vector獲得不是線程安全的,因此需要同步。先進的for循環使用迭代器,所以注意:

final Vector<Double> vector = new Vector<Double>(); 
vector.add(1.0); 
vector.add(2.0); 

// Not thread safe! If another thread modifies "vector" during the loop, then 
// a ConcurrentModificationException will be thrown. 
for (Double num : vector) { 
    System.out.println(num); 
} 

// You can try this as a quick fix, but it might not be what you want: 
synchronized (vector) { // "vector" must be final 
    for (Double num : vector) { 
     System.out.println(num); 
    } 
} 
+0

優秀 - 不知何故,我從來沒有遇到過線程。中斷() - 明天我可以使用它! – 2009-09-14 05:22:36

+7

首先,調用future.cancel(true)完全沒有任何作用。 invokeAll的合約聲明它將在返回之前取消任務,並且實現使用最後的塊來確保它。其次,不要調用Thread.interrupted(),這樣做會清除線程的中斷狀態。大多數實現都想使用Thread.isInterrupted()。應該仔細檢查清除旗幟。第三,他不必處理InterruptedException,除非他使用鎖定方法,例如鎖定獲取,然後編譯器確保他是。 FutureTask將會捕獲異常。 – 2009-09-14 06:41:44

+1

@Tim Bender:你說得對:future.cancel(true)什麼都不做,由我自己測試。但我還沒有明白你認爲我應該怎麼做。 – Raffo 2009-09-14 08:50:28

0

ConcurrentModificationException最常見的情況是當vector在被迭代的同時被修改。通常這將在單個線程中完成。您需要在整個迭代過程中鎖定Vector(並注意不要死鎖)。

+0

是的,我知道爲什麼異常被拋出,但它不應該。迭代是在我發佈的代碼部分之後,因此,如果代碼運行良好,我不應該得到一個異常... – Raffo 2009-09-13 16:14:22

1

ConcurrentModificationException從您的來電tasks.clear(),而你的Exceutors被遍歷您tasksVector到來。您可以嘗試在您的ExecutorService上撥打shutdownNow()

+0

這似乎不工作... – Raffo 2009-09-13 17:41:33

0

fut.get()是一個阻塞調用,即使超時後,你會被阻塞,直到任務完成。如果你想盡可能接近5分鐘,你需要檢查中斷標誌,我建議你使用保留中斷狀態的Thread.isInterrupted()方法。如果你想立即停止並且不需要清理任何狀態,那麼拋出一個異常,這個異常將被未來捕獲並且以ExecutionException的形式表示給你。

fut.cancel(true)不會執行任何操作,因爲invokeAll()方法已經爲您執行了此操作。

除非您在其他地方使用「任務」集合,否則您可能不需要對其調用clear()。這不會是你的問題的根源,因爲在你調用clear()的時候invokeAll()方法是通過List完成的。但是,如果您需要開始形成要執行的新任務列表,我建議您形成新的任務列表,而不是使用舊任務列表中的新任務。

不幸的是,我沒有你的問題的答案。我沒有看到足夠的信息來診斷它。您提供的代碼片段中沒有任何內容表示對類庫/方法的使用不恰當(只是不必要的)。也許如果你包含完整的堆棧跟蹤,而不是一行錯誤。

+0

我在其他地方使用了集合,並且它在while循環中,所以需要清除以便在循環重複時爲空。當然,我可以在帖子中顯示的代碼之後執行clear(),這應該沒問題。 我的問題的重要部分不是例外:我需要知道的是如何在5分鐘後停止未來,當然,我會嘗試按照您的建議拋出異常。我甚至可以改變我提交任務的方式。我在這裏學到了這種方式:http://stackoverflow.com/questions/1322147/help-with-java-executors-wait-for-task-termination – Raffo 2009-09-14 19:27:23

-1

fut.cancel(true);在finally塊

+0

你的意思是在一個被添加的'finally'塊,對吧? – 2015-10-21 08:37:09