Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Set timeout for threads in a ThreadPool

I want to set timeouts for threads which are executed within a thread pool. At the moment I have following code:

ExecutorService executor = Executors.newFixedThreadPool(8);
for(List<String> l: partition) {            
    Runnable worker = new WorkerThread(l);
    executor.execute(worker);
}       

executor.shutdown();
while (!executor.isTerminated()) {

}

The code just splits a big list of objects into sublists and process these sublist within single threads. But this is not the point.

I want to give each single thread in the thread pool a timeout. For only one thread in the pool I found following solution:

Future<?> future = null;

for (List<String> l : partition) {
    Runnable worker = new WorkerThread(l);
    future = executor.submit(worker);
}

try {
    System.out.println("Started..");
    System.out.println(future.get(3, TimeUnit.SECONDS));
    System.out.println("Finished!");
} catch (TimeoutException e) {
    System.out.println("Terminated!");
}

But this would not work for more than one thread. Maybe I have to put each thread in a List<Future> list and iterate over this list and set a timeout for each future object?

Any suggestions?

EDIT AFTER USING CountDownLatch:

CountDownLatch doneSignal = new CountDownLatch(partition.size());
List<Future<?>> tasks = new ArrayList<Future<?>>();
ExecutorService executor = Executors.newFixedThreadPool(8);
for (List<String> l : partition) {
    Runnable worker = new WorkerThread(l);
    tasks.add(executor.submit(doneSignal, worker));
}

doneSignal.await(1, TimeUnit.SECONDS);
if (doneSignal.getCount() > 0) {
    for (Future<?> fut : tasks) {
    if (!fut.isDone()) {
        System.out.println("Task " + fut + " has not finshed!");
        //fut.cancel(true) Maybe we can interrupt a thread this way?!
    }
    }
}

Works good so far.

So next question is how to interrupt a thread which is timed out? I try fut.cancel(true) and add following construct in some critical loops in the worker thread:

if(Thread.interrupted()) {
    System.out.println("!!Thread -> " + Thread.currentThread().getName() + " INTERRUPTED!!");
        return;
}

So the worker thread is "killed" after the timeout. Is this a good solution?

Furthermore: Is it possible to get the name of the thread which timed out over the Future interface? At the moment I have to print out the name in the if condition of the Thread.interrupted() construct.

Thanks for help!

Regards

like image 404
sk2212 Avatar asked Jan 09 '13 13:01

sk2212


1 Answers

Have you seen this? ExecutorService.invokeAll

It should be exactly what you want: Invoke a bundle of workers and have them timeout if taking too long.

EDIT after comment - (new idea): You can use a CountDownLatch to wait for the tasks to finish AND timeout via await(long timeout, TimeUnit unit)! You can then even do a shutdownNow and see which tasks have taken too long ...

EDIT 2:

To make it clearer:

  1. Have a CountDownLatch be count down by each Worker, when finished.
  2. In the main execution thread await with timeout on said latch.
  3. When that call returns, you can check the Latches's count to see if there has been the timeout hit (if it is >0).
  4. a) count = 0, all tasks finished in time. b) if not, loop the Futures and check their isDone. You don't have to call shutdown on the ExecutorService.
  5. Call shutdown if you do not need the Executor any longer.

Note: Workers can finish in the meantime between the timeout and calling their Future's isDone().

like image 155
Fildor Avatar answered Oct 24 '22 12:10

Fildor