views:

781

answers:

3

I'm using java.util.Timer to schedule a periodic task. At one point, I'd like to shut it down, and wait for it to finish.

Timer.cancel() will prevent any future tasks from running. How do I make sure any tasks are not running at the moment (or wait for them if they are?)

I can introduce external synchronization mechanisms, but I don't see how they can cover all cases. For example, if I synchronize on some Monitor within the task, I still miss the case when the task just started executing but didn't take the monitor.

What is the recommended practice for waiting until all tasks are really done, including currently running tasks?

A: 

The doc says: Terminates this timer, discarding any currently scheduled tasks. Does not interfere with a currently executing task (if it exists).

Zed
So ... how is that an answer to my question?I would indeed like to wait for the currently executing task, for a set amount of time.
ripper234
Why do you want to wait for it? Wait before you cancel the timer, or wait because of something completely different?
Zed
I want to write a single piece of code that, after it's run, guarantees that no tasks are left on the timer, either pending or running.
ripper234
Ah okay. You wrote "periodic task", which to me means that the task is repeated indefinitely, or in other words, there will always be tasks on the timer.
Zed
+7  A: 

You would be better using an ScheduledExecutorService instead of a Timer to schedule your periodic task. ScheduledExecutorService provides a shutdown() method that will execute any pending tasks. You can then call awaitTermination() to wait for shutdown() to finish.

Mark
+1. Or, if you have to use a `Timer` for some reason, then you can use a Condition (http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Condition.html) to synchronize two threads on an event (such as completion of the scheduled task).
Vinay Sajip
Yeah, item 68 in Effective Java (2nd ed) recommends ScheduledThreadPoolExecutor as a more flexible replacement for Timer. +1 for mentioning this, as recommended practices were asked for.
Jonik
Yeah, I should really read that book.
ripper234
A: 

Something like below might help your needs-

import java.util.Timer;
import java.util.TimerTask;

public class TimerGracefulShutdown {
    public static void main(String[] args) throws InterruptedException {
     //This is a synchronization helper class
     SyncHelper syncHelper = new SyncHelper();

     TimerManager myTimerManager = new TimerManager(syncHelper);

     //Try stopping timer after 5 seconds (it wont stop until the 30 seconds sleep of timertask does not finish)
     Thread.currentThread().sleep(5000);
     System.out.println("Going to stop my timer now");
     myTimerManager.stopTimer();
     System.out.println("Cancelled timer");
    }
}

class TimerManager {

    SyncHelper syncHelper;
    Timer timer;

    public TimerManager(SyncHelper syncHelper) {
     this.syncHelper = syncHelper;
     startTimer();
    }

    private void startTimer() {
     timer = new Timer(true);
     TimerTask myTask = new MyTimerTask(syncHelper);
     timer.scheduleAtFixedRate(myTask, 0, 100000);
    }

    public void stopTimer() {
     try {
      syncHelper.testAndSetOrReset("acquire");
     } catch(Exception e) {
      e.printStackTrace();
     }

     //Shutdown the timer here since you know that your timertask is not executing right now.
     timer.cancel();
     try {
      syncHelper.testAndSetOrReset("release");
     } catch (Exception e) {
      e.printStackTrace();
     }
    }

}

class MyTimerTask extends TimerTask {

    private SyncHelper syncHelper;

    public MyTimerTask(SyncHelper syncHelper) {
     this.syncHelper = syncHelper;
    }

    public void run() {
     try {
      syncHelper.testAndSetOrReset("acquire");
     } catch (Exception e1) {
      e1.printStackTrace();
     }

     System.out.println("Over here");
     try {
      Thread.currentThread().sleep(30000);
     } catch(Exception e) {

     }
     System.out.println("Done sleeping");

     //Finally release the helper.
     try {
      syncHelper.testAndSetOrReset("release");
     } catch (Exception e) {
      e.printStackTrace();
     }
    }

}

class SyncHelper {

    private int index = 0;

    public synchronized void testAndSetOrReset(String command) throws Exception {

     if("acquire".equals(command)) { 
      if(index == 1) {
       wait();
      }
      index++;
     } else if("release".equals(command)) {
      index--;
      notifyAll();
     }
    }
}
Keshav
You should use `Thread.sleep(...)` instead of `Thread.currentThread().sleep(...)` because it's a static method; your code might tempt you to do `someOtherThread.sleep(...)` which does not sleep `someOtherThread`.
newacct