How do I get FutureTask to return after TimeoutException?
Asked Answered
W

3

9

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


}
Wiles answered 15/8, 2009 at 5:29 Comment(0)
J
12

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;
}
Jamesjamesian answered 15/8, 2009 at 5:59 Comment(5)
!task.cancelled() should be !isCancelled() as djna originally wroteJeroboam
I think it's much better to use interruptions. First, your callable code doesn't need to know about task at all. Second, when you're using various blocking operations in your callable code (like Thread.sleep()), these will not react to task.isCancelled(), but usually react to interruptions. So using cancel(true) and making your code aware of interruptions is usually the best way to do this. (Your code will be also more general, because interruptions mechanism is so widely used in Java)Gorky
I guess my point is that "Interruption is a cooperative mechanism." (ibm.com/developerworks/java/library/j-jtp05236.html)Gorky
@Peter Nice article! I've changed the answer accordingly.Jamesjamesian
cleary code is not tested. task would be undefined in catch blockBakst
S
4

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.

Septima answered 15/8, 2009 at 6:30 Comment(0)
J
2

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

Jeroboam answered 15/8, 2009 at 5:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.