2

I need to create async thread that runs once with a delay of 2 minutes and that can be killed at any moment. I saw several possible solutions:

  1. ScheduledExecutorService and FutureTask allow me to interrupt a running task, but I will have to invoke shutdown() to terminate all the running threads, and this will block user until the processes were terminated. Also, I will have to frequently invoke Thread.interrupted() as described in Enno Shioji's answer.
  2. Timer and TimerTask do not require to release running threads, but I have no way to interrupt a running timer thread (Timer.cancel() just cancels future scheduling)
  3. Using Thread and sleep with thread interruption problem.

Is there a good solution? (I'm using tomcat 7)

thank you

Community
  • 1
  • 1
lili
  • 1,866
  • 8
  • 29
  • 50

3 Answers3

1

For your scenario 2 with TimerTask, why not just return from the run() method after calling this.cancel()?

Here's a snippet from something I wrote. I use the same technique whenever the tool encounters a situation that would make further execution invalid, like a misconfiguration.

...
if ( count < 1 ) {
   logger.error("CANCELING THREAD FOR " + host + ":" + jmxPort + "! " +
                "- CONFIGURATION INCOMPLETE - DUMP_COUNT must be 1 or greater.");
   this.cancel();
   return;
}
...
Keiichi
  • 11
  • 2
1

After some tests and researches, FutureTask.cancel() and Threads need similar handling of interrupts, as stated in Enno Shioji's answer

  1. Check interruption flag in your logic
  2. Act upon Interrupted exception

An example that tests interruption flag:

private final class MyTask implements Runnable {

    public void run() {
        try{
            for(int j=0; j<100000000; j++) {
                for(int i=1; i<1000000000; i++){
                    if(Thread.interrupted()){ //Don't use Thread.interrupt()!
                        Log.debug("Thread was interrupted for" + cache);
                        return; //Stop doing what you are doing and terminate.
                    }
                    Math.asin(0.1565365897770/i);
                    Math.tan(0.4567894289/i);
                }
            }                            
        }catch(Throwable e){//if exception is uncaught, the scheduler may not run again 
            ...
        }
    }
}

As I understand, ScheduledExecutorService maybe be shutdown when application ends running

Community
  • 1
  • 1
lili
  • 1,866
  • 8
  • 29
  • 50
0

Using Option 1 you can use FutureTask.cancel() with mayInterruptIfRunning parameter set to true to cancel your tasks.

The ScheduledExecutorService.scheduleAtFixedRate() creates a ScheduledFuture, which still can be canceled trough the FutureTask api.

Here is a naive test I've used to verify that task gets canceled. I suppose the worker thread may not be interrupted if you do some blocking IO (network or disk), but I haven't tested it. If cancel is called while task is not running, it all stops nicely, but if task is running when cancel is called the executor will try to kill the thread.

public static void main(String[] args) throws InterruptedException {
    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(2);

    ScheduledFuture<?> future = executor.scheduleAtFixedRate(new Runnable() {
        int i = 0;
        public void run() {
            int j = i++;
            System.err.println("Run " + j);

            try {
                Thread.sleep(5000L);
            } catch (InterruptedException e) {
                System.err.println("Interrupted " + j);
            }
        }

    }, 1000L, 2000L, TimeUnit.MILLISECONDS);

    Thread.sleep(10000L);
    System.err.println("Canceled " + future.cancel(true));

    Thread.sleep(20000L);

    executor.shutdownNow();
    System.err.println("Finished");
}
Eugene Kuleshov
  • 31,461
  • 5
  • 66
  • 67
  • right, but there are 2 drawbacks. First, if I start a long process in thread, I have to refer the isInterrupted flag constantly; second, I must invoke shutdown to release running threads, and this will block user until thread finished running – lili Mar 28 '12 at 19:27
  • The FutureTask returned by ExecutorService abstracts you from working with Threads. As per JavaDoc, it will take care of interrupting worker thread if you pass true for mayInterruptIfRunning. – Eugene Kuleshov Mar 28 '12 at 19:34
  • Not exactly according to Enno Shioji's answer: http://stackoverflow.com/questions/2903342/why-thread-started-by-scheduledexecutorservice-schedule-never-quits – lili Mar 28 '12 at 19:36
  • I updated my answer with additional references to ScheduledFuture javaDoc that explicitly says that scheduled task can be canceled. – Eugene Kuleshov Mar 28 '12 at 19:49
  • thanks, I will test it. But still, this approach requires me to invoke ScheduledExecutorService.shutdown() in the end to release threads. I do not want to block user and wait until all the threads finished running – lili Mar 28 '12 at 19:54
  • Not sure that "end" you're referring to. Cancel happens on ScheduledFuture instance, that will deal with a worker thread for this particular task. If there is something else scheduled you don't chancel trough ScheduledFuture, those supposedly shut down at the application shutdown... – Eugene Kuleshov Mar 28 '12 at 20:12
  • If I dont invoke ScheduledExecutorService.shutdown, thread that was not canceled may be reused next time? Or it will remain in memory? – lili Mar 28 '12 at 20:19
  • Regarding the cancellation issue: I built a test with task.cancel(true) and I see that the task is not canceled, even though cancel() return true – lili Mar 28 '12 at 22:31
  • You'll have to post your test. I did my own experiments and it is working as I expected. See above. – Eugene Kuleshov Mar 29 '12 at 18:52
  • Your test works because you invoke Thread.sleep. Future.cancel() fires interrupt, and sleep knows to be interrupted by InterruptionException. If instead of sleep you build a long loop inside another loop, it will not be interrupted by default. I'll add an example to my post – lili Mar 29 '12 at 20:40
  • Naturally it won't terminate. However when that long loop finishes task execution will stop. – Eugene Kuleshov Mar 29 '12 at 20:51
  • But if I need to stop it in the middle of the loop, I need to add logic to the thread loop – lili Mar 29 '12 at 20:53
  • I should point out that your original question don't have anything about long running or blocking tasks. Don't know your specifics, but if your periodical task runs too long, maybe you need to consider changing it. – Eugene Kuleshov Mar 29 '12 at 22:06
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/9485/discussion-between-lili-and-eugene-kuleshov) – lili Mar 30 '12 at 01:04