Waiting for a Timer to finish in Java
Asked Answered
K

3

14

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?

Kowtow answered 24/8, 2009 at 10:37 Comment(0)
S
25

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.

Stridulous answered 24/8, 2009 at 11:0 Comment(5)
+1. Or, if you have to use a Timer for some reason, then you can use a Condition (java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/…) to synchronize two threads on an event (such as completion of the scheduled task).Clung
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.Xerox
Or use a CountdownLatch etc. #289934Memnon
ScheduledExecutorService or its implementation ScheduledThreadPoolExecutor are not "replacements" for Timer. The methods ScheduledThreadPoolExecutor#scheduleWithFixedDelay(...) and Timer#schedule(...) (documented as "repeated fixed-delay execution") behave fundamentally different and thus ScheduledThreadPoolExecutor has no replacement for this method. This anwer is invalid for people who rely on Timer#schedule(...). I explained the difference more detailed as part of this answer https://mcmap.net/q/828924/-singlethreadscheduledexecutor-or-timer-scheduleatfixedrate-but-don-39-t-catch-up.Pyre
@Pyre I see your point but the question being asked was how to schedule a task, shut it down and wait for it to finish which ScheduleExecutorService does support.Stridulous
T
1

This is my solution with ScheduledExecutorService:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

...
public void startScheduler() {
    ScheduledExecutorService timerService = Executors.newSingleThreadScheduledExecutor();
    MyTimer myTimer = new MyTimer(timerService);

    timerService.scheduleAtFixedRate(myTimer, 1, 1, TimeUnit.SECONDS);
    try {
        boolean isTimerDone = timerService.awaitTermination(22, TimeUnit.SECONDS);
        if(!isTimerDone) {
            timerService.shutdown();
        }
        ...
    } catch (InterruptedException e) {
        ...
    }
}
...

And

import java.util.TimerTask;
import java.util.concurrent.ScheduledExecutorService;

@RequiredArgsConstructor
public class MyTimer extends TimerTask {
    private final ScheduledExecutorService scheduledExecutorService;           

    @Override
    public void run() {
        ...
        if(whenMyBusinessDone) {
            this.cancel();
            scheduledExecutorService.shutdown();
        }
    }
}
Tyro answered 8/12, 2022 at 10:56 Comment(0)
P
0

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();
        }
    }
}
Praemunire answered 24/8, 2009 at 12:38 Comment(1)
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.Easy

© 2022 - 2024 — McMap. All rights reserved.