2011-12-26 81 views
2

我已經嘗試了許多不同的方法來立即停止使用ExecutorService開始的任務,但沒有運氣。如何立即停止使用ExecutorService啓動的任務?

Future<Void> future = executorService.submit(new Callable<Void>(
    public Void call() { 
     ... do many other things here.. 
     if(Thread.currentThread.isInterrupted()) { 
      return null; 
     } 
     ... do many other things here.. 
     if(Thread.currentThread.isInterrupted()) { 
      return null; 
     } 
    } 
)); 

if(flag) { // may be true and directly cancel the task 
    future.cancel(true); 
} 

有時候,我需要取消任務,它剛開始後,你可能會好奇,爲什麼我要做到這一點,那麼你可以想像一個塞納里奧,用戶不小心點擊「下載」按鈕開始「下載任務」,他立即想取消該操作,因爲這只是一次意外點擊。

的問題是,調用future.cancel後(真),任務沒有停止,Thread.currentThread.isInterrupted()仍然返回,我也沒有辦法知道任務停止從call()方法中。

我想在設置一個像取消= true之後調用future.cancel(true)並在call()方法中不斷檢查該標誌,我認爲這是一個破解,代碼可能非常難看因爲用戶可以在同一時刻開始很多任務。

有沒有一種更優雅的方式來實現我想要的?

編輯:

這真的把我氣得火冒三丈。我已經在這個問題上花了差不多一天的時間了。我將嘗試爲我面臨的問題多解釋一點。

我按照以下步驟啓動5個任務,每個任務將啓動5個線程下載一個文件。然後我立即停止所有5個任務。對於下面的所有方法調用,我開始一個線程(ExecutorService.submit(task)),使它成爲異步的,就像你可以從方法的後綴中看到的那樣。

int t1 = startTaskAysnc(task1); 
int t2 = startTaskAysnc(task2); 
int t3 = startTaskAysnc(task3); 
int t4 = startTaskAysnc(task4); 
int t5 = startTaskAysnc(task5); 

int stopTaskAysnc(t1); 
int stopTaskAysnc(t2); 
int stopTaskAysnc(t3); 
int stopTaskAysnc(t4); 
int stopTaskAysnc(t5); 

startTaskAysnc(),我只是啓動套接字連接到遠程服務器,以獲取該文件的大小(這當然是要花一些時間),成功後獲得的檔案大小,我將開始5個線程下載文件的不同部分。像下面的(代碼簡化,使其更易於遵循):

public void startTaskAsync(DownloadTask task) { 
    Future<Void> future = executorService.submit(new Callable<Void>(
     public Void call() { 
      // this is a synchronous call 
      int fileSize = getFileSize(); 
      System.out.println(Thread.currentThread.isInterrupted()); 
      .... 
      Future<Void> futures = new Future<Void>[5]; 
      for (int i = 0; i < futures.length; ++i) { 
       futures[i] = executorService.submit(new Callable<Void>(){...}); 
      } 

      for (int i = 0; i < futures.length; ++i) { 
       futures[i].get(); // wait for it to complete 
      }    
     } 
    )); 
    synchronized (mTaskMap) { 
     mTaskMap.put(task.getId(), future); 
    } 
} 

public void stopTaskAysnc(int taskId) { 
    executorService.execute(new Runnable(){ 
     Future<Void> future = mTaskMap.get(taskId); 
     future.cancel(true); 
    }); 
} 

我注意到一個奇怪的行爲,我叫stopTaskAsync()後,所有5個任務,總會有至少一個任務得到停止(即Thread.currentThread.isInterrupted()返回true),其他4個任務保持運行。

我試着通過設置一個UncaughtExceptionHandler來嘗試你的建議,但沒有任何結果。

編輯:

的問題是在這個環節解決:Can't stop a task which is started using ExecutorService

回答

0

我想你會發現解決方案here。重點是取消方法引發InterruptedException。請檢查您的線程在取消後是否仍在運行?你確定你沒有試圖中斷完成的線程嗎?你確定你的線程沒有與其他異常失敗嗎?嘗試設置UncaughtExceptionHandler。

+0

嗨,@asdasd,我編輯了我的問題,如果您有線索,請給我一些幫助。謝謝。 – neevek 2011-12-26 08:34:19

4

嗯,Future.cancel(boolean)javadoc說:

如果任務已經啓動,則mayInterruptIfRunning 參數決定是否執行此任務的線程應該 在試圖停止任務中斷。

因此很確定執行任務的線程是中斷。什麼可能發生的是,的

一個......做很多其他的事情在這裏..

意外清除Threadinterrupted狀態,而不執行所需的 處理。如果你在Thread.interrupt()中設置斷點,你可能會抓到罪犯。

我能想到的另一個選擇是任務在捕獲中斷之前終止,或者是因爲它已完成或拋出一些未捕獲的異常。致電Future.get()確定。無論如何,正如asdasd提到的那樣,設置UncaughtExceptionHandler是一個好習慣。

+0

嗨,@yair,我編輯了我的問題,如果你有線索,請給我一些幫助。謝謝。 – neevek 2011-12-26 08:34:02

2

你正在做什麼是非常危險的:你使用一個線程池來執行任務(我會打電話給下載者),和相同的線程池來執行任務,其

  • 等待下載者來完成(我稱之爲控制器)
  • 或要求控制器停止

這意味着,如果控制器開始後到達線程的核心數,下載者將被放置在線程池隊列和控制器線程永遠不會完成。同樣,如果在執行取消任務時達到線程的核心數量,則取消任務將放入隊列中,直到其他任務完成時纔會執行。

您應該爲下載程序使用線程池,爲控制器使用另一個線程池,並使用當前線程取消控制器。

+0

嗨,JB Nizet。你是對的,在發佈這個問題之前,我已經意識到了這一點,但是我面對的問題似乎與使用線程池的策略沒有任何關係,因爲我確保了最少的線程數足以運行所有的線程控制器和下載5個任務。無論如何,我會採取你的建議,並使用2個獨立的線程池來運行控制器和下載程序,並取消當前線程中的控制器。 – neevek 2011-12-26 10:38:32