6

In a java class I have a method that sometimes takes a long time for execution. Maybe it hangs in that method flow. What I want is if the method doesn't complete in specific time, the program should exit from that method and continue with the rest of flow.

Please let me know is there any way to handle this situation.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Prathap
  • 1,023
  • 7
  • 19
  • 33

4 Answers4

8

You must use threads in order to achieve this. Threads are not harmful :) Example below run a piece of code for 10 seconds and then ends it.

public class Test {
    public static void main(String args[])
        throws InterruptedException {

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("0");
                method();
            }
        });
        thread.start();
        long endTimeMillis = System.currentTimeMillis() + 10000;
        while (thread.isAlive()) {
            if (System.currentTimeMillis() > endTimeMillis) {
                System.out.println("1");
                break;
            }
            try {
                System.out.println("2");
                Thread.sleep(500);
            }
            catch (InterruptedException t) {}
        }


    }

    static void method() {
        long endTimeMillis = System.currentTimeMillis() + 10000;
        while (true) {
            // method logic
            System.out.println("3");
            if (System.currentTimeMillis() > endTimeMillis) {
                // do some clean-up
                System.out.println("4");
                return;
            }
        }
    }
}
Muhammad Imran Tariq
  • 22,654
  • 47
  • 125
  • 190
1

Execute the method in a different thread, you can end a thread at anytime.

COD3BOY
  • 11,964
  • 1
  • 38
  • 56
  • Is there any way with out using Threads.I don't want to use Threads. – Prathap Jan 24 '12 at 05:44
  • @Rana _I don't want to use Threads_ Why? If youre not familiar, have a look at the [java tutorial](http://docs.oracle.com/javase/tutorial/essential/concurrency/procthread.html) – COD3BOY Jan 24 '12 at 05:47
  • Hi Sanjay, the problem with the threads is that we are not sure about the execution of thread flow if we have multiple threads running for same portion of code which seems to be a bit complex to manage or debug. – Prathap Jan 24 '12 at 05:53
  • Have a look at `java.util.concurrent.*` especially `FutureTask`, `Callable` and `Executors`. See this thread answer for an example: [StackOverflow Question 240320](http://stackoverflow.com/questions/240320/how-can-i-wrap-a-method-so-that-i-can-kill-its-execution-if-it-exceeds-a-specifi/241534#241534). – Alex Jan 24 '12 at 06:10
  • @Rana _multiple threads running for same portion of code_ ?? BTW, have a look at the tutorial I linked in the previous comment. – COD3BOY Jan 24 '12 at 07:39
0

Based on the above snipplet, I tried creating a glorified spring bean.

Such executor runs the passed limitedRuntimeTask in limited runtimeInMs. If the task finishes within its time limits, the caller continues normally in execution.

If the limitedRuntimeTask fails to finish in the defined runtimeInMs, the caller will receive the thread execution back. If a timeBreachedTask was defined, it will be executed before returning to caller.

public class LimitedRuntimeExecutorImpl {


public void runTaskInLessThanGivenMs(int runtimeInMs, final Callable limitedRuntimeTask, final Callable timeBreachedTask) {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                LOGGER.info("Started limitedRuntimeTask");
                limitedRuntimeTask.call();
                LOGGER.info("Finished limitedRuntimeTask in time");
            } catch (Exception e) {
                LOGGER.error("LimitedRuntimeTask exception", e);
            }
        }
    });
    thread.start();

    long endTimeMillis = System.currentTimeMillis() + runtimeInMs;

    while (thread.isAlive()) {
        if (System.currentTimeMillis() > endTimeMillis) {
            LOGGER.warn("LmitedRuntimeTask did not finish in time (" + runtimeInMs + ")ms. It will run in vain.");
            if(timeBreachedTask != null ){
                try {
                    LOGGER.info("Executing timeBreachedTask");
                    timeBreachedTask.call();
                    LOGGER.info("Finished timeBreachedTask");
                } catch (Exception e) {
                    LOGGER.error("timeBreachedTask exception", e);
                }
            }
            return;
        }
        try {
            Thread.sleep(10);
        }
        catch (InterruptedException t) {}
    }

}

}

Abax
  • 1
0

I feel the approach in accepted answer is a bit outdated. With Java8, it can be done much simpler.

Say, you have a method

MyResult conjureResult(String param) throws MyException { ... }

then you can do this (keep reading, this is just to show the approach):

private final ExecutorService timeoutExecutorService = Executors.newSingleThreadExecutor();

MyResult conjureResultWithTimeout(String param, int timeoutMs) throws Exception {
    Future<MyResult> future = timeoutExecutorService.submit(() -> conjureResult(param));
    return future.get(timeoutMs, TimeUnit.MILLISECONDS);
}    

of course, throwing Exception is bad, here is the correct extended version with proper error processing, but I suggest you examine it carefully, your may want to do some things differently (logging, returning timeout in extended result etc.):

private final ExecutorService timeoutExecutorService = Executors.newSingleThreadExecutor();

MyResult conjureResultWithTimeout(String param, int timeoutMs) throws MyException {
    Future<MyResult> future = timeoutExecutorService.submit(() -> conjureResult(param));
    try {
        return future.get(timeoutMs, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
        //something interrupted, probably your service is shutting down
        Thread.currentThread().interrupt();
        throw new RuntimeException(e);
    } catch (ExecutionException e) {
        //error happened while executing conjureResult() - handle it
        if (e.getCause() instanceof MyException) {
            throw (MyException)e.getCause();
        } else {
            throw new RuntimeException(e);
        }
    } catch (TimeoutException e) {
        //timeout expired, you may want to do something else here
        throw new RuntimeException(e);
    }
}
Alex Pakka
  • 9,466
  • 3
  • 45
  • 69
  • 1
    I know this is 4 years late to the party, but the way this is set up means the thread from your ExecutorService stays alive. Which could result in some unwanted behaviour. – SvenT23 Feb 04 '21 at 12:52
  • @SvenT23 Yes, but it is ready to process another submit(). Obviously, it should be shut down on some application shutdown hook, shutdown listener, etc.. ideally, a thread pool should be used with a shutdown down the road, but the thread pool lifecycle management is kind of beyond the scope of the question. Still, good comment, and good reminder for people not to forget about it. – Alex Pakka Feb 04 '21 at 18:31