Stefza
Stefza

Reputation: 277

Accurately scheduling a task to run in Java

I'm having a few issues with scheduling a task to run in my code every minute.

I have tried many ways to do this such as:

Timer timer = new Timer();

timer.schedule( new TimerTask() {
    public void run() {
       printTime();
    }
 }, 0, 60*1000);

And also:

ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
Runnable task = () -> {
    printTime();
};
executor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.MINUTES);

These both print out erratic times not even close to the times I have provided:

2019-12-03T12:48:23.300
2019-12-03T12:48:24.700
2019-12-03T12:48:41.895
2019-12-03T12:48:42.799
2019-12-03T12:48:47.189
2019-12-03T12:48:47.211
2019-12-03T12:48:47.278

These are the times I get, totally wrong and some are within seconds! Is there an accurate way for me to schedule a task to run every minute while my code preforms other tasks?

My plan was to calculate the times between when the task is fired but Timer and ScheduledExecutor seems to wait longer than the time provided also not only shorter. So this means it would also be off.

Upvotes: 0

Views: 1453

Answers (2)

savas
savas

Reputation: 366

I run below code and it is working as expected

public class Test {
    public static void main(String[] args)  {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        Runnable task = () -> printTime();
        executor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.MINUTES);
    }

    private static void printTime() {
        System.out.println(new Date());
    }
}

Output is:

Tue Dec 03 16:06:31 EET 2019
Tue Dec 03 16:07:31 EET 2019
Tue Dec 03 16:08:31 EET 2019
Tue Dec 03 16:09:31 EET 2019

Probably, you are running multiple instance of TimerTask. You can extract your code to an utility class to ensure you have only one instance.

    public enum  PrintTimer {
        INSTANCE;

        private boolean running = false;

        private ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

        public synchronized void start()  {
            if (!running) {
                running = true;
                Runnable task = () -> printTime();
                executor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.MINUTES);
            }
        }

        private static void printTime() {
            System.out.println(LocalDateTime.now());
        }
    }

You can call it like:

PrintTimer.INSTANCE.start();

Upvotes: 2

Butiri Dan
Butiri Dan

Reputation: 1772

Both of them works

Runnable task = () -> System.out.println("Executor: " + LocalDateTime.now());
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);

executor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.MINUTES);

and

new Timer().schedule(new TimerTask() {
    public void run() {
        System.out.println("Timer: " + LocalDateTime.now());
    }
}, 0, 60*1000);

Output

Timer: 2019-12-03T15:29:25.931
Executor: 2019-12-03T15:29:25.931
Timer: 2019-12-03T15:30:25.904
Executor: 2019-12-03T15:30:25.934
Timer: 2019-12-03T15:31:25.904
Executor: 2019-12-03T15:31:25.935
Timer: 2019-12-03T15:32:25.905
Executor: 2019-12-03T15:32:25.937

If you can choose, better use ScheduledExecutorService (Java Timer vs ExecutorService)

Upvotes: 1

Related Questions