Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I get FutureTask to return after TimeoutException?

In the code below, I'm catching a TimeoutException after 100 seconds as intended. At this point I would expect the code to exit from main and the program to terminate but it keeps printing to the console. How do I get the task to stop executing after timeout?

private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();

private static <T> T timedCall(Callable<T> c, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
    FutureTask<T> task = new FutureTask<T>(c);
    THREAD_POOL.execute(task);
    return task.get(timeout, timeUnit);
}


public static void main(String[] args) {

    try {
        int returnCode = timedCall(new Callable<Integer>() {
            public Integer call() throws Exception {
                for (int i=0; i < 1000000; i++) {
                    System.out.println(new java.util.Date());
                    Thread.sleep(1000);
                }
                return 0;
            }
        }, 100, TimeUnit.SECONDS);
    } catch (Exception e) {
        e.printStackTrace();
        return;
    }


}
like image 315
deltanovember Avatar asked Aug 15 '09 05:08

deltanovember


3 Answers

You need to cancel your task on timeout (and interrupt its thread). That's what cancel(true) method is for. :

private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool();

private static <T> T timedCall(FutureTask<T> task, long timeout, TimeUnit timeUnit) throws InterruptedException, ExecutionException, TimeoutException {
    THREAD_POOL.execute(task);
    return task.get(timeout, timeUnit);
}


public static void main(String[] args) {
        try {
            FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
                public Integer call() throws Exception {
                        for (int i=0; i < 1000000; i++) {
                                if (Thread.interrupted()) return 1;
                                System.out.println(new java.util.Date());
                                Thread.sleep(1000);
                        }
                        return 0;
                }
            });
            int returnCode = timedCall(task, 100, TimeUnit.SECONDS);
        } catch (Exception e) {
                e.printStackTrace();
                task.cancel(true);
        }
        return;
}
like image 153
Alexey Romanov Avatar answered Oct 16 '22 03:10

Alexey Romanov


Your Callable must to be able to stop quickly, when needed.

Your code:

public Integer call() throws Exception {
    for (int i=0; i < 1000000 && !task.cancelled(); i++) {
        System.out.println(new java.util.Date());
        Thread.sleep(1000); // throws InterruptedException when thread is interrupted
    }
    return 0;
}

Is already able to do that thanks to calling Thread.sleep(). Point is that futureTask.cancel(true) will interrupt other thread, and your code needs to react to this interruption. Thread.sleep() does that. If you didn't use Thread.sleep() or other interruptible blocking code, you would have to check Thread.currentThread().isInterrupted() by yourself, and quit as soon as possible (e.g. by throwing new InterruptedException()) when you find this to be true.

You need to call futureTask.cancel(true); from your exception handler to cancel and interrupt thread which runs your task.

My advice is to learn about interruption mechanism (this is great article: Dealing with InterruptedException), and use it.

like image 4
Peter Štibraný Avatar answered Oct 16 '22 02:10

Peter Štibraný


Once you caught the TimeoutException, you need to call the cancel(true) method of your task ...

or shut down your ExecutorService by calling shutdownNow() ...

or quit the VM by calling System.exit(0)

depending on your needs

like image 2
Zed Avatar answered Oct 16 '22 04:10

Zed