2013-04-16 50 views
0

我使用ExecutorService實現了TimeoutTask。在下面的方法中,我提交TimeoutTask,如果它在給定的時間內超時,我取消任務並關閉執行程序。使用ExecutorService的TimeoutTask

private boolean waitForProcessToBeCompleted(long timeOut) { 
      boolean result = false; 
      ExecutorService executor = Executors.newSingleThreadExecutor(); 
      // Create a FutureTask which will be run 
      FutureTask<Boolean> futureTask = new FutureTask<Boolean>(new TimeoutTask()); 
      executor.submit(futureTask); // Run the FutureTask 
      try { 
       result = futureTask.get(timeOut, TimeUnit.MILLISECONDS); // Check if FutureTask completed in the given time 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } catch (ExecutionException e) { 
       e.printStackTrace(); 
      } catch (TimeoutException e) { 
       futureTask.cancel(true); 
       result = true; // Return True only when timed out 
      } finally { 
       executor.shutdownNow(); // Stop the executor 
      } 
      return result; 
     } 

它運行得很好,我沒有任何問題。

但是,我想知道這是否是最好的代碼設計。我只是想知道使用由ExecutorService.submit()返回的Future來獲取Callable的返回值還是超時TimeoutTask會更好。例如

  Future<?> futureTask = executor.submit(new TimeoutTask()); // Run the FutureTask 
      try { 
       result = futureTask.get(timeOut, TimeUnit.MILLISECONDS); // Check if FutureTask completed in the given time 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } catch (ExecutionException e) { 
       e.printStackTrace(); 
      } catch (TimeoutException e) { 
       futureTask.cancel(true); 
       result = true; // Return True only when timed out 
      } finally { 
       executor.shutdownNow(); // Stop the executor 
      } 
      return result; 

我正在使用JDK7。

回答

0

我寧願使用CountDownLatch

List<List<String>> elements = MyPartition.partition(bigObjectList, size); 
List<Future<?>> tasks = new ArrayList<Future<?>>(); 
ExecutorService executor = Executors.newSingleThreadExecutor(); 
CountDownLatch doneSignal = new CountDownLatch(10); 
for(List<String> l: elements) {   
    ReadTask worker = new ReadTask(doneSignal, l); 
    tasks.add(executor.submit(worker)); 
} 

long timeout = 10000; 
doneSignal.await(timeout, TimeUnit.SECONDS); 
boolean notFinished = false; 
if(doneSignal.getCount() > 0) { 
    for(Future<?> fut : tasks) { 
    if(!fut.isDone()) { 
     System.out.println("Sub Thread " + fut + " has not finshed!"); 
     fut.cancel(true);    
     notFinished = true; 
    } 
    } 
} 
0

如果你看看futureTask.cancel的代碼,你會看到,它只是試圖中斷正在執行任務的線程。如果任務regullary顯式或隱式地檢查中斷標誌(通過調用sleep()或wait()),則此中斷可能有效。在Java中,沒有其他安全的方法來停止執行方法。

因此,您可以實現相同的功能,而無需每次創建單獨的單線程執行程序。相反,從waitForProcessToBeCompleted方法中執行TimerTask。爲了通知超時,請將觀看任務提交給SheduledExecutorService。觀察任務應該中斷執行TimerTask的線程。如果任務在超時前完成,則取消觀看任務。

這樣你需要一個SheduledExecutorService,但它消耗很少的處理器週期,並且可以在整個應用程序中重用。

+0

謝謝阿列克謝。但是,在上面的代碼設計中,我可以使用submit()返回的Future來獲取結果,而不是使用單獨的FutureTask。那有意義嗎? – ParagJ

+0

它沒有意義,因爲通過submit(Callable)返回的Future實際上是FutureTask。這兩種方法可以做到這一點。 –

+0

再次感謝。但是我只是想知道如果我使用submit()方法返回的Future對象,我不需要明確地聲明它。我只是使用返回值。不管怎樣,謝謝。 – ParagJ

0

ExecutorService上的invokeAll方法可用於自動取消超過超時的任務。這可以讓你在不關閉線程池的情況下取消任務(如果你願意的話,你可以重新使用同一個線程池來做其他事情)。

private boolean waitForProcessToBeCompleted(long timeOut) { 
    ExecutorService executor = Executors.newSingleThreadExecutor(); 
    List<FutureTask> tasks = new ArrayList<>(); 
    tasks.add(new SomeFutureTaskThing())); 
    List<Future<Boolean>> results; 
    try { 
    results = executor.invokeAll(tasks, timeOut, TimeUnit.SECONDS); 
    } catch (InterruptedException e) { 
    Thread.currentThread().interrupt(); // Restore interrupt status. 
    return null; 
    } catch (ExecutionException e) { 
    throw new RuntimeException(e.getCause()); 
    } 
    Future<Boolean> result = results.get(0); 
    try { 
    return result.get(); 
    } catch (CancellationException e) { 
    System.err.println("Timed out"); 
    return null; 
    } 
}