85

Is it possible to force Java to throw an Exception after some block of code runs longer than acceptable?

13 Answers 13

57

Here's the simplest way that I know of to do this:

final Runnable stuffToDo = new Thread() {
  @Override 
  public void run() { 
    /* Do stuff here. */ 
  }
};

final ExecutorService executor = Executors.newSingleThreadExecutor();
final Future future = executor.submit(stuffToDo);
executor.shutdown(); // This does not cancel the already-scheduled task.

try { 
  future.get(5, TimeUnit.MINUTES); 
}
catch (InterruptedException ie) { 
  /* Handle the interruption. Or ignore it. */ 
}
catch (ExecutionException ee) { 
  /* Handle the error. Or ignore it. */ 
}
catch (TimeoutException te) { 
  /* Handle the timeout. Or ignore it. */ 
}
if (!executor.isTerminated())
    executor.shutdownNow(); // If you want to stop the code that hasn't finished.

Alternatively, you can create a TimeLimitedCodeBlock class to wrap this functionality, and then you can use it wherever you need it as follows:

new TimeLimitedCodeBlock(5, TimeUnit.MINUTES) { @Override public void codeBlock() {
    // Do stuff here.
}}.run();
Sign up to request clarification or add additional context in comments.

6 Comments

Just came across this. What is the overhead for doing something along these lines? I feel like if you do something in stuffToDo a lot, creating a new single thread executor every time is costly but I don't know hence my question.
I've never encountered a performance problem with this approach. In some cases it may be preferable to create your own implementation of Executor, if you think you can make a more lightweight version by focusing specifically on the case of having just one thread.
The performance of ExecutorService was assessed favorably here (stackoverflow.com/a/27025552/2116890), where it was determined to be awesome. In that case they were dealing with more threads. In my experience, whether using a single thread or many, I've never noticed the overhead, but I've also made no effort to in any way measure its performance relative to an alternative.
After executor.shutdownNow(), you probably want to while (true) {try {if (executor.awaitTermination(1, TimeUnit.SECONDS)) break;} catch (InterruptedException ie) {}}, since actually stopping the task may take some time.
Interesting thing to note here. Getting a timeout does not mean the underlying task has been stopped. The future is cancelled and the executorService is aknowledged of that but the thread may still be working on without caring about the termination which means that it might still wast resources you would have liked to be freed.
|
47

I compiled some of the other answers into a single utility method:

public class TimeLimitedCodeBlock {

  public static void runWithTimeout(final Runnable runnable, long timeout, TimeUnit timeUnit) throws Exception {
    runWithTimeout(new Callable<Object>() {
      @Override
      public Object call() throws Exception {
        runnable.run();
        return null;
      }
    }, timeout, timeUnit);
  }

  public static <T> T runWithTimeout(Callable<T> callable, long timeout, TimeUnit timeUnit) throws Exception {
    final ExecutorService executor = Executors.newSingleThreadExecutor();
    final Future<T> future = executor.submit(callable);
    executor.shutdown(); // This does not cancel the already-scheduled task.
    try {
      return future.get(timeout, timeUnit);
    }
    catch (TimeoutException e) {
      //remove this if you do not want to cancel the job in progress
      //or set the argument to 'false' if you do not want to interrupt the thread
      future.cancel(true);
      throw e;
    }
    catch (ExecutionException e) {
      //unwrap the root cause
      Throwable t = e.getCause();
      if (t instanceof Error) {
        throw (Error) t;
      } else if (t instanceof Exception) {
        throw (Exception) t;
      } else {
        throw new IllegalStateException(t);
      }
    }
  }

}

Sample code making use of this utility method:

  public static void main(String[] args) throws Exception {
    final long startTime = System.currentTimeMillis();
    log(startTime, "calling runWithTimeout!");
    try {
      TimeLimitedCodeBlock.runWithTimeout(new Runnable() {
        @Override
        public void run() {
          try {
            log(startTime, "starting sleep!");
            Thread.sleep(10000);
            log(startTime, "woke up!");
          }
          catch (InterruptedException e) {
            log(startTime, "was interrupted!");
          }
        }
      }, 5, TimeUnit.SECONDS);
    }
    catch (TimeoutException e) {
      log(startTime, "got timeout!");
    }
    log(startTime, "end of main method!");
  }

  private static void log(long startTime, String msg) {
    long elapsedSeconds = (System.currentTimeMillis() - startTime);
    System.out.format("%1$5sms [%2$16s] %3$s\n", elapsedSeconds, Thread.currentThread().getName(), msg);
  }

Output from running the sample code on my machine:

    0ms [            main] calling runWithTimeout!
   13ms [ pool-1-thread-1] starting sleep!
 5015ms [            main] got timeout!
 5016ms [            main] end of main method!
 5015ms [ pool-1-thread-1] was interrupted!

10 Comments

Can you please add some sample on how to use it? Despite my settings it throws the exception as soon as I execute the runWithTimeout method, even if I set the timeout to 150 minutes.
I now added sample code and improved original code to cancel the already submitted task in case of timeout. I would appreciate an upvote ;-)
@htf, this should really be the accepted answer.. Nicely done
@AliReza19330, yes, my bad. You're right. Starting a new thread per user might not be the best idea, performance-wise, though. :-P
@NeemePraks I think " throw (Exception) e;" should be " throw (Exception) t;" in the second runWithTimeout method
|
27

Yes, but its generally a very bad idea to force another thread to interrupt on a random line of code. You would only do this if you intend to shutdown the process.

What you can do is to use Thread.interrupt() for a task after a certain amount of time. However, unless the code checks for this it won't work. An ExecutorService can make this easier with Future.cancel(true)

Its much better for the code to time itself and stop when it needs to.

2 Comments

The problem is that I have a third-party library which runs too long sometimes and there's no native mechanism for timing out
This is a common problem unfortunately and the only reliable way to manage this is to have a seperate process which you can kill. The alternative is to use Thread.stop() on it. Read the warnings for this method before you use it!
10

If it is test code you want to time, then you can use the time attribute:

@Test(timeout = 1000)  
public void shouldTakeASecondOrLess()
{
}

If it is production code, there is no simple mechanism, and which solution you use depends upon whether you can alter the code to be timed or not.

If you can change the code being timed, then a simple approach is is to have your timed code remember it's start time, and periodically the current time against this. E.g.

long startTime = System.currentTimeMillis();
// .. do stuff ..
long elapsed = System.currentTimeMillis()-startTime;
if (elapsed>timeout)
   throw new RuntimeException("tiomeout");

If the code itself cannot check for timeout, you can execute the code on another thread, and wait for completion, or timeout.

    Callable<ResultType> run = new Callable<ResultType>()
    {
        @Override
        public ResultType call() throws Exception
        {
            // your code to be timed
        }
    };

    RunnableFuture<ResultType> future = new FutureTask<>(run);
    ExecutorService service = Executors.newSingleThreadExecutor();
    service.execute(future);
    ResultType result = null;
    try
    {
        result = future.get(1, TimeUnit.SECONDS);    // wait 1 second
    }
    catch (TimeoutException ex)
    {
        // timed out. Try to stop the code if possible.
        future.cancel(true);
    }
    service.shutdown();
}

Comments

4

I can suggest two options.

  1. Within the method, assuming it is looping and not waiting for an external event, add a local field and test the time each time around the loop.

    void method() {
        long endTimeMillis = System.currentTimeMillis() + 10000;
        while (true) {
            // method logic
            if (System.currentTimeMillis() > endTimeMillis) {
                // do some clean-up
                return;
            }
        }
    }
    
  2. Run the method in a thread, and have the caller count to 10 seconds.

    Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                method();
            }
    });
    thread.start();
    long endTimeMillis = System.currentTimeMillis() + 10000;
    while (thread.isAlive()) {
        if (System.currentTimeMillis() > endTimeMillis) {
            // set an error flag
            break;
        }
        try {
            Thread.sleep(500);
        }
        catch (InterruptedException t) {}
    }
    

The drawback to this approach is that method() cannot return a value directly, it must update an instance field to return its value.

2 Comments

+0: In your second example, are you trying to do a Thread.join(long) with a timeout ;)
After thread.start() you could have thread.join(10000); instead of the rest of the code.
3

EDIT: Peter Lawrey is completely right: it's not as simple as interrupting a thread (my original suggestion), and Executors & Callables are very useful ...

Rather than interrupting threads, you could set a variable on the Callable once the timeout is reached. The callable should check this variable at appropriate points in task execution, to know when to stop.

Callables return Futures, with which you can specify a timeout when you try to 'get' the future's result. Something like this:

try {
   future.get(timeoutSeconds, TimeUnit.SECONDS)
} catch(InterruptedException e) {
   myCallable.setStopMeAtAppropriatePlace(true);
}

See Future.get, Executors, and Callable ...

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html#get-long-java.util.concurrent.TimeUnit-

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Callable.html

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool%28int%29

2 Comments

unfortunately, that only works if you control the code executing within the callable (which if that is true, then doing this is trivial)
ok, I can see that in @HTF's comment now. I guess Thread.stop() is the only way! See warnings here: download.oracle.com/javase/6/docs/technotes/guides/concurrency/…
2

I created a very simple solution without using any frameworks or APIs. This looks more elegant and understandable. The class is called TimeoutBlock.

public class TimeoutBlock {

 private final long timeoutMilliSeconds;
    private long timeoutInteval=100;

    public TimeoutBlock(long timeoutMilliSeconds){
        this.timeoutMilliSeconds=timeoutMilliSeconds;
    }

    public void addBlock(Runnable runnable) throws Throwable{
        long collectIntervals=0;
        Thread timeoutWorker=new Thread(runnable);
        timeoutWorker.start();
        do{ 
            if(collectIntervals>=this.timeoutMilliSeconds){
                timeoutWorker.stop();
                throw new Exception("<<<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated.");
            }
            collectIntervals+=timeoutInteval;           
            Thread.sleep(timeoutInteval);

        }while(timeoutWorker.isAlive());
        System.out.println("<<<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds.");
    }

    /**
     * @return the timeoutInteval
     */
    public long getTimeoutInteval() {
        return timeoutInteval;
    }

    /**
     * @param timeoutInteval the timeoutInteval to set
     */
    public void setTimeoutInteval(long timeoutInteval) {
        this.timeoutInteval = timeoutInteval;
    }
}

example :

try {
        TimeoutBlock timeoutBlock = new TimeoutBlock(10 * 60 * 1000);//set timeout in milliseconds
        Runnable block=new Runnable() {

            @Override
            public void run() {
                //TO DO write block of code to execute
            }
        };

        timeoutBlock.addBlock(block);// execute the runnable block 

    } catch (Throwable e) {
        //catch the exception here . Which is block didn't execute within the time limit
    }

This was so much useful for me when i had to connect to a FTP account. Then download and upload stuff. sometimes FTP connection hangs or totally breaks. This caused whole system to go down. and i needed a way to detect it and prevent it from happening . So i created this and used it. Works pretty well.

Comments

2

I faced a similar kind of issue where my task was to push a message to SQS within a particular timeout. I used the trivial logic of executing it via another thread and waiting on its future object by specifying the timeout. This would give me a TIMEOUT exception in case of timeouts.

final Future<ISendMessageResult> future = 
timeoutHelperThreadPool.getExecutor().submit(() -> {
  return getQueueStore().sendMessage(request).get();
});
try {
  sendMessageResult = future.get(200, TimeUnit.MILLISECONDS);
  logger.info("SQS_PUSH_SUCCESSFUL");
  return true;

} catch (final TimeoutException e) {
  logger.error("SQS_PUSH_TIMEOUT_EXCEPTION");
}

But there are cases where you can't stop the code being executed by another thread and you get true negatives in that case.

For example - In my case, my request reached SQS and while the message was being pushed, my code logic encountered the specified timeout. Now in reality my message was pushed into the Queue but my main thread assumed it to be failed because of the TIMEOUT exception. This is a type of problem which can be avoided rather than being solved. Like in my case I avoided it by providing a timeout which would suffice in nearly all of the cases.

If the code you want to interrupt is within you application and is not something like an API call then you can simply use

future.cancel(true)

However do remember that java docs says that it does guarantee that the execution will be blocked.

"Attempts to cancel execution of this task. This attempt will fail if the task has already completed, has already been cancelled,or could not be cancelled for some other reason. If successful,and this task has not started when cancel is called,this task should never run. If the task has already started,then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted inan attempt to stop the task."

Comments

1

If you want a CompletableFuture way you could have a method like

public MyResponseObject retrieveDataFromEndpoint() {

   CompletableFuture<MyResponseObject> endpointCall 
       = CompletableFuture.supplyAsync(() ->
             yourRestService.callEnpoint(withArg1, withArg2));

   try {
       return endpointCall.get(10, TimeUnit.MINUTES);
   } catch (TimeoutException 
               | InterruptedException 
               | ExecutionException e) {
       throw new RuntimeException("Unable to fetch data", e);
   }
}

If you're using spring, you could annotate the method with a @Retryable so that it retries the method three times if an exception is thrown.

Comments

1

Instead of having the task in the new thread and the timer in the main thread, have the timer in the new thread and the task in the main thread:

public static class TimeOut implements Runnable{
    public void run() {
        Thread.sleep(10000);
        if(taskComplete ==false) {
            System.out.println("Timed Out");
            return;
        }
        else {
            return;
        }
    }
}
public static boolean taskComplete = false;
public static void main(String[] args) {
    TimeOut timeOut = new TimeOut();
    Thread timeOutThread = new Thread(timeOut);
    timeOutThread.start();
    //task starts here
    //task completed
    taskComplete =true;
    while(true) {//do all other stuff }
}

3 Comments

Your variable TimeOut timeOut in the main() method is never used.
In main method it should be Thread timeOutThread = new Thread(timeOut); then @Johannes timeOut would be used.
note, this is the only solution that scales. you really only need a single timer thread, and that could interrupt thousands of worker threads. the other way around, you got a thousands of timer threads for thousands of worker threads - not ideal to say the least.
0

There is a hacky way to do it.

Set some boolean field to indicate whether the work was completed. Then before the block of code, set a timer to run a piece of code after your timeout. The timer will check if the block of code had finished executing, and if not, throw an exception. Otherwise it will do nothing.

The end of the block of code should, of course, set the field to true to indicate the work was done.

2 Comments

This is not 100% correct, because the timer thread will be independent of thread running the block of code. The timer thread can throw an exception although the 'worker' thread did nothing, because there were other threads with higher priority. There is a slight difference between 'runs longer than X seconds' and 'has started before X seconds'.
Okay, fair enough. Thanks for the response. I'll leave the answer up so others can see your comment
0

There's a very simple option that nobody's mentioned yet:

Duration timeout = Duration.ofMinutes(5);
Thread thread = new Thread(() -> {
    // your code here
});
thread.start();
thread.join(timeout.toMillis());
if (thread.isAlive()) {
    thread.interrupt();
    throw new MyTimeoutException();
}

If the thread running your code block fails to complete within the timeout, it is interrupted and whatever exception you want can be thrown.

It is possible to write code that will simply ignore the interruption and carry on. If you're dealing with this can cannot fix it then there is thread.stop(), but that can break any synchronisation mechanisms that you are relying on. See its deprecation notice.

You can also capture exceptions from the thread:

AtomicReference<Throwable> uncaughtException = new AtomicReference<>();
thread.setUncaughtExceptionHandler((t, ex) -> uncaughtException.setRelease(ex));

// ...

Throwable ex = uncaughtException.getAcquire();
if (ex != null) {
    throw ex;
}

Comments

-1

I had this problem too, my logs print out with ‘’Unexpected end of stream‘’.and ‘’Could not get a resource from the pool‘’, I set the timeout of brpop to 30s, redis to 31s, and mysql database connection pool to 300s. For now, this error is not printed on the log, but I don't know if this error will be reported in the future.I don't know if it has a bad effect on my writing to the database

1 Comment

This does not really answer the question. If you have a different question, you can ask it by clicking Ask Question. To get notified when this question gets new answers, you can follow this question. Once you have enough reputation, you can also add a bounty to draw more attention to this question. - From Review

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.