0

I have implmented a TimeoutTask using ExecutorService. In the below method I am submitting the TimeoutTask and if it is timed out in a given time, I cancel the task and shuts down the executor.

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;
        }

It is running very well and I don't have any issue.

However, I would like to know whether this is the best code design. I was just wondering if it could have been better to use a Future returned by ExecutorService.submit() to get the return value of the Callable or to time out the TimeoutTask. e.g.

            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;

I am using JDK7.

ParagJ
  • 1,566
  • 10
  • 38
  • 56

3 Answers3

0

I would prefer using 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;
    }
  }
}
sk2212
  • 1,688
  • 4
  • 23
  • 43
0

If you look at the code of futureTask.cancel, you'll see that it just attempts to interrupt the thread which is executing the task. This interruption may work if the task regullary checks the interruption flag, explicitly or implicitly (via calling to sleep() or wait()). In Java, there is no other safe way to stop execution of a method.

So, you can implement the same functionality without creating each time a separate single-threaded executor. Instead, execute the TimerTask from within the waitForProcessToBeCompleted method. In order to be notified of timeout, submit a watching task to a SheduledExecutorService. The watching task should interrupt the thread which executes the TimerTask. If the task is completed before timeout, cancel the watching task.

This way you need a SheduledExecutorService, but it consumes very little processor cycles, and can be reused all over the application.

Alexei Kaigorodov
  • 13,189
  • 1
  • 21
  • 38
  • Thanks Alexei. However, in the above code design, instead of using a seperate FutureTask, I could have used a Future returned by submit() to get() the result. Does that make sense? – ParagJ Apr 16 '13 at 11:26
  • It does not make sense, as the Future returned by submit(Callable) is actually a FutureTask. These are 2 ways to do the same. – Alexei Kaigorodov Apr 16 '13 at 12:26
  • Thanks again. But I was just wondering if I use the Future object returned by submit(), I don't need to declare it explicitly. I am just using the return value. Anyway, thanks. – ParagJ Apr 16 '13 at 12:39
0

The invokeAll method on ExecutorService can be used to automatically cancel tasks that exceed the timeout. This lets you cancel tasks without shutting down the threadpool (which let's you re-use the same threadpool for other things if you choose).

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;
  }
}
dillon
  • 41
  • 2