2016-02-12 60 views
1

假設我們有可能長時間運行的任務:裝飾Java的調用添加行爲

public class LongRunningTask { 
    public ReturnType doSomething() { 
     ... 
    } 
} 

而且我們希望同時運行許多任務。

所以,我有我的調用它:

public class LongRunningCallable implements Callable<LongRunningTask> { 
    private final LongRunningTask task; 
    ... 
    public ReturnType call() { 
     return task.doSomething(); 
    } 
    ... 
} 

現在,因爲這可能真的持續多久,我minght要限制它只有一定量的運行。所以,我可能會做這樣的事情:

public class InterruptibleCallable<T> implements Callable<T> { 
     protected final long timeout; 
     protected final TimeUnit timeUnit; 
     protected final Callable<T> callable; 

     public InterruptibleCallable(long timeout, TimeUnit timeUnit, Callable<T> callable) { 
       this.timeout = timeout; 
       this.timeUnit = timeUnit; 
       this.callable = callable; 
     } 

     @Override 
     public T call() throws Exception { 

       ExecutorService executorService = Executors.newSingleThreadExecutor(); 

       T result = null; 

       try { 
        result = executorService.submit(callable).get(timeout, timeUnit); 
       } catch (InterruptedException e) { 
        LOGGER.error("Callable: " + callable.toString() + " was interrupted"); 
        throw e; 
       } catch (ExecutionException e) { 
        throw e; 
       } catch (TimeoutException e) { 
        LOGGER.warn("Callable: " + callable.toString() + " timed out after " + timeout + " " + timeUnit); 
        throw e; 
       } finally { 
        executorService.shutdown(); 
       } 

       return result; 
     } 
} 

這是確定的,但現在我也想換行,以便它可以重試本身(有延遲),如果它encounteres異常:

public class RetryableCallable<T> implements Callable<T> { 
    private final long delay; 
    private final TimeUnit timeUnit; 
    private final Callable<T> callable; 

    @Override 
    public T call() throws Exception { 
     T result = null; 

     try { 
      ExecutorService executorService = Executors.newSingleThreadExecutor(); 
      result = executorService.submit(this.callable).get(); 
     } catch (Exception e) {   
      ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); 
      result = executorService.schedule(this.callable, delay, timeUnit).get(); 
     } 

     return result; 
    } 
} 

現在,我的問題(S):

  1. 是否已有此capabilites的提供該庫(或超集)?

  2. 這是一個很好的設計,尤其是創建另一個執行器並在每個包裝器中提交一個可調用對象,爲什麼?

  3. 從設計模式視圖和性能視圖兩方面處理此問題的最佳方法是什麼?

謝謝:d

+1

爲什麼要在其名稱中具有「可調用」的類創建新的線程池?這聽起來太複雜了。 –

回答

1

RetryableCallable各地InterruptibleCallable它包裝LongRunningTask和每個RetryableCallable執行創建兩個額外的執行者是壞的。

RetryableCallable捕獲TimeoutException通常它不應該再次運行相同的任務。這有點令人困惑,因爲如果任務被超時「殺死」,爲什麼你想再運行一次? 另外爲什麼你需要在這裏創建另一個執行者?保持簡單

public class RetryableCallable<T> implements Callable<T> { 
    private int retries = 3; 

    @Override 
    public T call() throws Exception { 
     while (retries-- > 0) { 
      try { 
       return callable.call(); 
      } catch (InterruptedException e) { 
       LOGGER.error("Callable: " + callable.toString() + " was interrupted"); 
       throw e; 
      } catch (TimeoutException e) { 
       LOGGER.warn("Callable: " + callable.toString() + " timed out after " + timeout + " " + timeUnit); 
       throw e; 
      } catch (Exception e) { 
       LOGGER.warn("Callable: " + callable.toString() + " failed"); 
      } 
     } 
     throw new IllegalStateException();//or return null 
    } 
} 

,它不應該使用超時和延時,這RetryableCallable但不是RetryableCallableWithDelayAndTimeoutAndSomethingElse

如果要限制任務的執行時間,我看到至少有4個好辦法做到這一點:

  • 呼叫get(time, timeunit)螺紋其提交它call()函數內部
  • 極限任務的執行時間,例如通過檢查「定期」我們有更多的時間。
  • 使您自己的執行者類與其中的自定義執行器和一個線程審計員,這將接受一些TimeLimitedCallable其中延伸Callableint getTimeLimit()函數。審計員將控制執行TimeLimitedCallable的所有運行任務的時間範圍。
  • 爲「任務的審計人員」製作一個單獨的執行程序,並在向主執行人提交一個(或一堆)帶有業務邏輯的任務之後 - 創建審計人任務並將其提交給單獨的執行人。