Calvin Jones
Calvin Jones

Reputation: 367

Exit method after duration

I'm trying to figure out a way to exit a method call if it takes more then say 2 seconds to execute everything in its method's body. For example, I have a huge list of strings and want to iterate through the list but only iterate as much of the strings as I can in 2 seconds. Say I have 9,000 objects in the list but can only iterate through 6,500 in 2 seconds, the method should stop after 2 second. Is this even possible to accomplish? I searched and found how to stop a thread and timers (how to start a method after a certain duration) but nothing on how to exit a method after a duration.

public ArrayList<String> removeWords(){

   //Want some sort of timer here that stops method body after 2 seconds

   for(String current: words){

            words.remove(current);
   }
}

public static void main(String [] args){
        ArrayList<String> wordList = removeWords();
        System.out.println("Number of words in list "+ wordList.size());
}

Upvotes: 1

Views: 298

Answers (2)

Raffaele
Raffaele

Reputation: 20875

Premise: when iterating over a collection, to remove elements you must use the same iterator or you get an exception:

Iterator<String> it = words.iterator();
while (it.hasNext()) {
  String word = it.next();
  if (shouldRemove(word))
    it.remove();
}

If the program you want to stop is a loop and the single iteration does not contain anything that can block you can simply calculate the end instant and check after an X number of iterations:

for (String word : words) {
  if (System.currentTimeMillis() > endInstant) {
    timedOut = true; // better store this
    break;
  // ...
}

Otherwise the usual (and more robust) approach is wrapping your program in a Callable<E> and using a separate thread:

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Result> promise = executor.submit(callable);
Result result = promise.get(10, TimeUnits.SECOND);

This way the monitor gets an exception if the result is not ready by the allowed time.

As a final note, consider that the wrapped task will still be running and eating resources, so you may want to set a shouldStop flag to true from the main thread to stop the computation as early as possible.

Upvotes: 3

Andy Brown
Andy Brown

Reputation: 19161

Even interrupting a thread using Thread.interrupt() requires you to periodically check to see if an interrupt has been requested (using Thread.interrupted() or Thread.isInterrupted()), and then decide what to do about it.

So a good general pattern would be to :

  1. Accept a timeout (the usual pattern is long timeout, TimeUnit unit) in your processing method's parameters
  2. Periodically (every n iterations of your loop, or even every iteration if it doesn't hurt your performance) check for the timeout and for interruption (using System.currentTimeMillis() and TimeUnit.toMillis() to do the comparisons).
  3. return if either occurs

What you return if you timeout is up to you, but for interruption you could just throw a new InterruptedException().

Of course, if you aren't going to launch your method in a separate thread then you don't need to worry about interruption checking - just the timeouts.

Upvotes: 3

Related Questions