33

I want to delay doing something, along the lines of setting a countdown timer that will "do a thing" after a certain amount of time.

I want the rest of my program to keep running while I wait, so I tried making my own Thread that contained a one-minute delay:

public class Scratch {
    private static boolean outOfTime = false;

    public static void main(String[] args) {
        Thread countdown = new Thread() {
            @Override
            public void run() {
                try {
                    // wait a while
                    System.out.println("Starting one-minute countdown now...");
                    Thread.sleep(60 * 1000);

                    // do the thing
                    outOfTime = true;
                    System.out.println("Out of time!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        countdown.start();

        while (!outOfTime) {
            try {
                Thread.sleep(1000);
                System.out.println("do other stuff here");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}


While this worked, more-or-less, it seemed like there should be a better way of doing this.

After some searching, I found a bunch of questions like these but they don't really address what I'm trying to do:

I don't need anything this complicated; I just want to do a single thing after a certain amount of time while letting the rest of the program still run.

How should I go about scheduling a one-time task to "do a thing"?

Community
  • 1
  • 1
azurefrog
  • 10,785
  • 7
  • 42
  • 56

2 Answers2

44

While the java.util.Timer used to be a good way to schedule future tasks, it is now preferable1 to instead use the classes in the java.util.concurrent package.

There is a ScheduledExecutorService that is designed specifically to run a command after a delay (or to execute them periodically, but that's not relevant to this question).

It has a schedule(Runnable, long, TimeUnit) method that

Creates and executes a one-shot action that becomes enabled after the given delay.


Using a ScheduledExecutorService you could re-write your program like this:

import java.util.concurrent.*;

public class Scratch {
    private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
    public static void main(String[] args) {
        System.out.println("Starting one-minute countdown now...");
        ScheduledFuture<?> countdown = scheduler.schedule(new Runnable() {
            @Override
            public void run() {
                // do the thing
                System.out.println("Out of time!");
            }}, 1, TimeUnit.MINUTES);

        while (!countdown.isDone()) {
            try {
                Thread.sleep(1000);
                System.out.println("do other stuff here");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        scheduler.shutdown();
    }
}

One of the nice things you get by doing things this way is the ScheduledFuture<?> object you get back from calling schedule().

This allows you to get rid of the extra boolean variable, and just check directly whether the job has run.

You can also cancel the scheduled task if you don't want to wait anymore by calling its cancel() method.


1See Java Timer vs ExecutorService? for reasons to avoid using a Timer in favor of an ExecutorService.

Community
  • 1
  • 1
azurefrog
  • 10,785
  • 7
  • 42
  • 56
  • will this scheduled executor run once or every minute. I want mine to run just once and exit. – qualebs Jan 10 '18 at 07:30
  • @qualebs In this example the action will run just once. The time being passed into the `schedule()` method is the delay before the action is run. – azurefrog Jan 10 '18 at 15:56
  • @azurefrog yes, it's running just once and I hope the `countdown` object is getting the value from the `TimeUnit` provided here as 1 minute. How can this code be modified to run continuously every 1 minute?, I mean just the opposite way of your code behaviour right now – Suresh Apr 12 '18 at 05:38
  • Why not use a CountDownLatch to replace the count down object implemented by ScheduleService? – qtopierw Jul 19 '18 at 09:42
0

Thanks it worked for me. I used scheduler to schedule a task at a batchinterval calculated at runtime.

    manualTriggerBatchJob.setSchedulingProperties(pblId, batchInterval);
    ScheduledExecutorService scheduledExecutorService =
            Executors.newScheduledThreadPool(5);
    @SuppressWarnings("unchecked")
    ScheduledFuture scheduledFuture =
            scheduledExecutorService.schedule(manualTriggerBatchJob,
            batchIntervalInMin,TimeUnit.MILLISECONDS);