2012-08-01 69 views
8

我試圖創建一個方法,可以在最長的時間內執行給定的任務。如果在這段時間內沒有完成,在放棄之前應重試多次。它也應該在每次嘗試之間等待幾秒鐘。這是我提出的,我想對我的方法提出一些批評。他們是使用ScheduledExecutorService來做到這一點的更簡單的方法,還是我這樣做的方式就夠了嗎?Java執行任務時執行了多次重試和超時

public static <T> T execute(Callable<T> task, int tries, int waitTimeSeconds, int timeout) 
    throws InterruptedException, TimeoutException, Exception { 

    Exception lastThrown = null; 
    for (int i = 0; i < tries; i++) { 
     try { 
      final Future<T> future = new FutureTask<T>(task); 
      return future.get(timeout, TimeUnit.SECONDS); 
     } catch (TimeoutException ex) { 
      lastThrown = ex; 
     } catch (ExecutionException ex) { 
      lastThrown = (Exception) ex.getCause(); 
     } 
     Thread.sleep(TimeUnit.SECONDS.toMillis(waitTimeSeconds)); 
    } 
    if (lastThrown == null) { 
     lastThrown = new TimeoutException("Reached max tries without being caused by some exception. " + task.getClass()); 
    } 
    throw lastThrown; 
} 

回答

5

我想,但它是我的觀點,如果你正在計劃與網絡相關的任務時,你不應該重試,但最終在並行運行。稍後我會介紹這種方法。

關於您的代碼,您應該將任務傳遞給執行程序,或將FutureTask傳遞給線程。它不會產生線程或自行執行。如果你有一個執行者(見ExecutorService),你甚至不需要一個FutureTask,你可以簡單地調度它並獲得一個可調用的。

所以,因爲你有一個ExecutorService,您可以撥打:

Future<T> future = yourExecutor.submit(task); 

的Future.get(超時)會等待超時,並最終與TimeoutException異常返回任務就算從來沒有下開始的,例如,如果Executor已經忙於做其他工作並且找不到空閒線程。所以,你最終可能會嘗試5次並等待幾秒鐘,而不會讓任務有機會運行。這可能是也可能不是你期望的,但通常不是。也許你應該等待它開始,然後再給它一個超時時間。

此外,即使拋出TimeoutException,您也應該明確地取消Future,否則它可能會繼續運行,因爲當超時獲取失敗時,文檔或代碼都不會停止運行。

即使您取消它,除非Callable已被「正確書寫」,它可能會持續運行一段時間。沒有什麼可以在這部分代碼中做到這一點,只要記住沒有線程可以「真正停止」另一個線程在Java中做什麼,並且有很好的理由。

但是,我想你的任務將主要是網絡相關的,所以它應該正確地對線程中斷作出反應。

我通常使用不同的策略是這樣的情況:

  1. 我會寫公共靜態牛逼執行(可調用任務,詮釋maxTries,INT超時),所以任務,嘗試的最大數量(可能1 ),最大總超時時間(「無論您嘗試多少次,10秒或什麼都不需要,我最多需要10秒鐘的回答」)
  2. 我開始產卵任務,將其交給執行者,然後調用將來。獲得(超時/嘗試)
  3. 如果我收到結果,請將其退回。如果我收到例外情況,請再試一次(請參閱後面的內容)
  4. 如果我得到超時,我不會取消未來,而是將它保存在列表中。
  5. 我檢查是否有太多的時間過去了,或者是太多的重試。在這種情況下,我取消列表中的所有期貨並拋出異常,返回空值,無論如何
  6. 否則,我循環,再次安排任務(與第一個並行)。
  7. 看點二
  8. 如果我沒有收到結果,我在列表中選中未來的(一個或多個),上一催生任務也許一個成功地做到這一點。

假設你的任務可以被執行多次(因爲我想他們是,否則沒有辦法重試),對於網絡的東西,我發現這個解決方案更好地工作。

假設您的網絡實際上非常繁忙,您需要網絡連接,每個網絡連接20次,每次2秒。由於您的網絡繁忙,20個重試中沒有一個管理在2秒內獲得連接。但是,持續40秒的單個執行可能會設法連接和接收數據。這就像一個人在網頁緩慢的時候在頁面上強迫f5一樣,它不會有任何好處,因爲每次瀏覽器必須從頭開始。

相反,我保持各種期貨運行,第一個管理獲取數據將返回一個結果,其他將停止。如果第一個掛起,第二個掛起,或者第三個掛起。

與瀏覽器相比,就像打開另一個選項卡並重新嘗試在不關閉第一個頁面的情況下加載頁面那樣。如果網絡很慢,第二個網絡會花費一些時間,但不會停止第一個,最終會正確加載。如果第一個選項卡掛起,則第二個選項卡將快速加載。無論先載哪一個,我們都可以關閉另一個標籤。

1

調用execute的線程會阻塞很長時間。不知道這對你是否正確。基本上,對於這些類型的任務,ScheduledExecutorService是最好的。您可以安排任務並指定時間。看看ScheduledThreadPoolExecutor