19

I have one simple question regarding Java TimerTask. How do I pause/resume two TimerTask tasks based on a certain condition? For example I have two timers that run between each other. When a certain condition has been met inside the task of first timer, the first timer stops and starts the second timer, and the same thing happens when a certain condition has been met inside the task of second timer. The class below shows exactly what I mean:

public class TimerTest {
    Timer timer1;
    Timer timer2;
    volatile boolean a = false;

    public TimerTest() {
        timer1 = new Timer();
        timer2 = new Timer();     
    }

    public void runStart() {
        timer1.scheduleAtFixedRate(new Task1(), 0, 1000);
    }

    class Task1 extends TimerTask {
        public void run() {
            System.out.println("Checking a");
            a = SomeClass.getSomeStaticValue();

            if (a) {
                // Pause/stop timer1, start/resume timer2 for 5 seconds
                timer2.schedule(new Task2(), 5000);
            }
        }
    }

    class Task2 extends TimerTask{
        public void run() {
            System.out.println("Checking a");
            a = SomeClass.getSomeStaticValue();

            if (!a) {
                // Pause/stop timer2, back to timer1
                timer1.scheduleAtFixedRate(new Task1(), 0, 1000);
            }

            // Do something...
        }
    }

    public static void main(String args[]) {
        TimerTest tt = new TimerTest();
        tt.runStart();      
    }
}

So my question is, how do I pause timer1 while running timer2 and vice versa while timer2 is running? Performance and timing is my main concern as this needs to be implemented inside another running thread. By the way I am trying to implement these concurrent timers on Android.

Thanks for your help!

Vaibhav Mule
  • 5,016
  • 4
  • 35
  • 52
Faiz
  • 517
  • 2
  • 7
  • 10
  • 2
    Exception in thread "Timer-1" java.lang.IllegalStateException: Timer already cancelled. – Faiz Jan 20 '10 at 02:23
  • You might want to think about moving over to `java.util.concurrent`. `Timer` is considered a bit old school. – Tom Hawtin - tackline Jan 20 '10 at 02:34
  • I edited from stopping/restarting to pausing/resuming the timers. Is it possible using `Thread.sleep(milliseconds)`? – Faiz Jan 20 '10 at 03:12
  • I realize this is just sample code and you're onto bigger things. But in this example, you would not use two timers, but one, with appropriate if conditions dictating what behavior to do inside the run method. Many times one thinks in many timers and many TimerTasks, when you only need that if you've got some kind of periodicity that requires it (one is 20ms, another is every 2 minutes). Even in that case, I wouldn't pause, as I say in my answer. I would just change what the run method does. Unless, of course, they're doing something which is time sensitive (e.g., running a chronograph). – Dan Rosenstark Jan 20 '10 at 03:17
  • Tom Hawtin, how is Timer old school? I've seen people fight with thread based concepts to solve realtime problems that can be solved easily with timers. Timer is alive and well, and threads are great for threading. – Dan Rosenstark Jan 20 '10 at 03:21
  • @yar I want to perform two different tasks based on single condition at different time rate. For example let's say `timer1` checks `a` for every second and if `a` is true, then `timer2` starts performing something for 5 seconds, stops and continue checking `a` for every second. – Faiz Jan 20 '10 at 03:34
  • I re-edited again the code to closely show what exactly I am trying to do. Sorry for the confusion! – Faiz Jan 20 '10 at 03:46
  • @Faiz, this is clear. My point was that even with different periodicities -- 5 seconds and 2 seconds -- I would just schedule two fixed-rate tasks on one timer. Unless it's absolutely crucial that the other timer start counting seconds FROM WHEN IT'S SWITCHED ON. If not, you just keep the tasks running and alter only their run methods. – Dan Rosenstark Jan 20 '10 at 10:55

8 Answers8

25

From TimerTask.cancel():

Note that calling this method from within the run method of a repeating timer task absolutely guarantees that the timer task will not run again.

So once cancelled, it won't ever run again. You'd be better off instead using the more modern ScheduledExecutorService (from Java 5+).

Edit: The basic construct is:

ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
exec.scheduleAtFixedRate(runnable, 0, 1000, TimeUnit.MILLISECONDS);

but looking into it there's no way of cancelling that task once its started without shutting down the service, which is a bit odd.

TimerTask might be easier in this case but you'll need to create a new instance when you start one up. It can't be reused.

Alternatively you could encapsulate each task as a separate transient service:

final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
Runnable task1 = new Runnable() {
  public void run() {
    a++;
    if (a == 3) {
      exec.shutdown();
      exec = Executors.newSingleThreadScheduledExecutor();
      exec.scheduleAtFixedRate(task2, 0, 1000, TimeUnit.MILLISECONDS)
    }
  }
};
exec.scheduleAtFixedRate(task1, 0, 1000, TimeUnit.MILLISECONDS);
bentech
  • 1,226
  • 15
  • 19
cletus
  • 616,129
  • 168
  • 910
  • 942
  • 1
    Could you show me how to implement the `ScheduledExceutorService` in the class above? thanks – Faiz Jan 20 '10 at 02:31
  • It's not the best idea to shutdown the executor service, you can cancel a task via ScheduledFuture returned by scheduleAtFixedRate() – axtavt Jan 20 '10 at 02:56
16

easiest solution i found: just add a boolean in the run code in the timer task, like so:

timer.schedule( new TimerTask() {
    public void run() {
       if(!paused){
           //do your thing
       }
    }
 }, 0, 1000 );
Song Keang
  • 357
  • 3
  • 7
  • 9
    I believe this is not efficient. Timer will keep on running and checking boolean value everytime. – Alex Jul 09 '16 at 21:16
  • 1
    I doubt efficiency is a concern, since in practice there's likely more time-consuming operations in the task than a single conditional. What concerns me about this approach is the task can fire at any time after being "resumed". This could be noticeable for long time intervals. – Jakob Jan 28 '17 at 16:43
  • this may serve the purpose, but kind on annoying solution , rather if we can track this process and , pause and resume on demand will be good – Asraful Sep 25 '18 at 13:36
7

If you have already canceled one timer, you can't re-start it, you'll have to create a new one.

See this answer, it contains a video and the source code how I did something similar.

Basically there are two method: pause and resume

In pause:

public void pause() {
    this.timer.cancel();
}

In resume:

public void resume() {
    this.timer = new Timer();
    this.timer.schedule( aTask, 0, 1000 );
}

That makes the perception of pause/resume.

If your timers perform different actions based on the state of the application you may consider use the StatePattern

Fist define a abstract state:

abstract class TaskState  {
    public void run();
    public TaskState next();
}

And provide as many states as you like. The key is that one state leads you to another.

class InitialState extends TaskState {
    public void run() {
        System.out.println( "starting...");
    }
    public TaskState next() {
         return new FinalState();
    }
 }
 class FinalState extends TaskState  {
     public void run() {
         System.out.println("Finishing...");
     }
     public TaskState next(){
         return new InitialState();
    }
 }

And then you change the state in your timer.

Timer timer = new Timer();
TaskState state = new InitialState();

timer.schedule( new TimerTask() {
     public void run() {
          this.state.run();
          if( shouldChangeState() ) {
              this.state = this.state.next();
           }
     }
 }, 0, 1000 );

Finally, if what you need is to perform the same thing, but at different rates, you may consider using the TimingFramework. It is a bit more complex but let's you do cool animations, by allowing the painting of certain component take place at different rates ( instead of being linear )

Community
  • 1
  • 1
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
6

In my opinion, this is somewhat misguided. If your code needs time guarantees, you can't use Timer anyway, nor would you want to. "This class does not offer real-time guarantees: it schedules tasks using the Object.wait(long) method."

The answer, IMHO, is that you don't want to pause and restart your timers. You just want to suppress their run methods from doing their business. And that's easy: you just wrap them in an if statement. The switch is on, they run, the switch is off, they miss that cycle.

Edit: The question has shifted substantially from what it was originally, but I'll leave this answer in case it helps anyone. My point is: if you don't care when your event fires in the N millisecond span (just that it doesn't EXCEED once every N milliseconds), you can just use conditionals on the run methods. This is, in fact, a very common case, especially when N is less than 1 second.

Dan Rosenstark
  • 68,471
  • 58
  • 283
  • 421
4

Reviewing your source code, here are the changes ( which pretty much validate my previous answer )

In task1:

// Stop timer1 and start timer2
timer1.cancel();
timer2 = new Timer(); // <-- just insert this line
timer2.scheduleAtFixedRate(new Task2(), 0, 1000);

and in task2:

// Stop timer2 and start timer1
timer2.cancel();
timer1 = new Timer(); // <-- just insert this other
timer1.scheduleAtFixedRate(new Task1(), 0, 1000);

It runs on my machine:

it works!

Glorfindel
  • 21,988
  • 13
  • 81
  • 109
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • 3
    nice background on the terminal window :) – Dan Rosenstark Jan 21 '10 at 12:05
  • Nice but What if I want change the period of two timers. Lets say the period of timer 1 is 5000 and timer 2 is 1000, then I guess this will screw up the whole code. In that case how would I be able to do it – Mehroze Yaqoob Mar 10 '14 at 19:10
3

Android won't reuse a TimerTask that has already been scheduled once. So it's necessary to reinstantiate both the Timer and TimerTask, for example like this in a Fragment:

private Timer timer;
private TimerTask timerTask;

public void onResume ()
{
    super.onResume();

    timer = new Timer();
    timerTask = new MyTimerTask();
    timer.schedule(timerTask, 0, 1000);
}

public void onPause ()
{
    super.onPause();
    timer.cancel(); // Renders Timer unusable for further schedule() calls.
}
Stephan Henningsen
  • 3,665
  • 1
  • 22
  • 29
0

I am able to stop a timer and a task using following code:

    if(null != timer)
    {

        timer.cancel();
        Log.i(LOG_TAG,"Number of cancelled tasks purged: " + timer.purge());
        timer = null;
    }

    if(task != null)
    {
        Log.i(LOG_TAG,"Tracking cancellation status: " + task.cancel());
        task = null;
    }
0
    Timer timer1;
    private boolean videoCompleteCDR=false;
    private boolean isVideoPlaying=false;   
    int videoTime=0;
    private int DEFAULT_VIDEO_PLAY_TIME = 30;  

    @Override
    public View onCreate(){
       isVideoPlaying = true; //when server response is successfully
    }


    @Override
    public void onPause() {
        super.onPause();

        if(isVideoPlaying ) {
            if(this.timer1 !=null) {
                this.timer1.cancel();
            }
        }
    }
    @Override
    public void onResume() {
        super.onResume();
        if(isVideoPlaying  && !videoCompleteCDR) {
            callTimerTask();
        }
    }

    @Override
    public void onHiddenChanged(boolean hidden) {
        super.onHiddenChanged(hidden);
        if (!hidden) {
            printLog( "GameFragment  visible ");
            if(isVideoPlaying  && !videoCompleteCDR) {
                callTimerTask();
            }

        } else {
            printLog("GameFragment in visible ");
            if(isVideoPlaying) {
                if(this.timer1 !=null) {
                    this.timer1.cancel();
                }
            }
        }
    }

    private void callTimerTask() {
        // TODO Timer for auto sliding
        printLog( "callTimerTask Start" );
        timer1 = new Timer();
        timer1.schedule(new TimerTask() {
            @Override
            public void run() {
                if (getActivity() != null) {
                    getActivity().runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            if (getActivity() == null) {
                                return;
                            }
                            videoTime++;
                            if(DEFAULT_VIDEO_PLAY_TIME ==videoTime){
                                videoCompleteCDR=true;
                                Log.e("KeshavTimer", "callTimerTask videoCompleteCDR called.... " +videoTime);
                                destroyTimer();
                            }
                            Log.e("KeshavTimer", "callTimerTask videoTime " +videoTime);
                        }
                    });
                } else {
                    printLog("callTimerTask getActivity is null ");
                }
            }
        }, 1000, 1000);
        // TODO  300, 2000;
    }


    private void destroyTimer(){
        this.timer1.cancel();
    }
Keshav Gera
  • 10,807
  • 1
  • 75
  • 53