How to run certain task every day at a particular time using ScheduledExecutorService?
As with the present java SE 8 release with it's excellent date time API with java.time
these kind of calculation can be done more easily instead of using java.util.Calendar
and java.util.Date
.
- Use date time class's i.e. LocalDateTime of this new API
- Use ZonedDateTime class to handle Time Zone specific calculation including Daylight Saving issues. You will find tutorial and example here.
Now as a sample example for scheduling a task with your use case:
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
ZonedDateTime nextRun = now.withHour(5).withMinute(0).withSecond(0);
if(now.compareTo(nextRun) > 0)
nextRun = nextRun.plusDays(1);
Duration duration = Duration.between(now, nextRun);
long initialDelay = duration.getSeconds();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(new MyRunnableTask(),
initialDelay,
TimeUnit.DAYS.toSeconds(1),
TimeUnit.SECONDS);
The initialDelay
is computed to ask the scheduler to delay the execution in TimeUnit.SECONDS
. Time difference issues with unit milliseconds and below seems to be negligible for this use case. But you can still make use of duration.toMillis()
and TimeUnit.MILLISECONDS
for handling the scheduling computaions in milliseconds.
And also TimerTask is better for this or ScheduledExecutorService?
NO: ScheduledExecutorService
seemingly better than TimerTask
. StackOverflow has already an answer for you.
From @PaddyD,
You still have the issue whereby you need to restart this twice a year
if you want it to run at the right local time. scheduleAtFixedRate
won't cut it unless you are happy with the same UTC time all year.
As it is true and @PaddyD already has given a workaround(+1 to him), I am providing a working example with Java8 date time API with ScheduledExecutorService
. Using daemon thread is dangerous
class MyTaskExecutor
{
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
MyTask myTask;
volatile boolean isStopIssued;
public MyTaskExecutor(MyTask myTask$)
{
myTask = myTask$;
}
public void startExecutionAt(int targetHour, int targetMin, int targetSec)
{
Runnable taskWrapper = new Runnable(){
@Override
public void run()
{
myTask.execute();
startExecutionAt(targetHour, targetMin, targetSec);
}
};
long delay = computeNextDelay(targetHour, targetMin, targetSec);
executorService.schedule(taskWrapper, delay, TimeUnit.SECONDS);
}
private long computeNextDelay(int targetHour, int targetMin, int targetSec)
{
LocalDateTime localNow = LocalDateTime.now();
ZoneId currentZone = ZoneId.systemDefault();
ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone);
ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec);
if(zonedNow.compareTo(zonedNextTarget) > 0)
zonedNextTarget = zonedNextTarget.plusDays(1);
Duration duration = Duration.between(zonedNow, zonedNextTarget);
return duration.getSeconds();
}
public void stop()
{
executorService.shutdown();
try {
executorService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException ex) {
Logger.getLogger(MyTaskExecutor.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Note:
MyTask
is an interface with functionexecute
.- While stopping
ScheduledExecutorService
, Always useawaitTermination
after invokingshutdown
on it: There's always a likelihood your task is stuck / deadlocking and the user would wait forever.
The previous example I gave with Calender was just an idea which I did mention, I avoided exact time calculation and Daylight saving issues. Updated the solution on per the complain of @PaddyD
Use ScheduledExecutorService to run code at specific Date time only once
You can use ScheduledExecutorService.schedule(Runnable command, long delay, TimeUnit unit)
long delay = afterOneMinute.getTime() - System.currentTimeMillis();
ScheduledExecutorService executorService = ...;
executorService.schedule(runnable, delay, TimeUnit.MILLISECONDS);
Run JAVA program at exact times repeatedly using ScheduledExecutorService
I could not find a way to schedule a Timer
with uneven intervals. However, it is relatively straightforward to schedule a Timer
to execute at fixed intervals.
For your case, you could schedule three timers, one for :15
, one for :25
, and one for :45
past the hour:
public class TaskTest {
public static scheduleTask(int interval) {
GregorianCalendar cal = new GregorianCalendar();
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH);
int date = cal.get(Calendar.DATE);
int hour = cal.get(Calendar.HOUR_OF_DAY);
int hourScheduled = hour;
// if we are past the scheduled time then schedule for the next hour
if (minute > interval) {
++hourScheduled;
}
cal.set(year, month, date, hourScheduled, interval);
long initialDelay = cal.getTimeInMillis() - System.currentTimeMillis();
if (initialDelay < 0) {
initialDelay = 0L;
}
// schedule each job for once per hour
int period = 60*60*1000;
Timer timer = new Timer();
SomeTask someTask = new SomeTask();
timer.scheduleAtFixedRate(someTask, initialDelay, period);
}
public static void main(String[] args) {
// schedule for the 15th, 25th and 45th min of time every hour
scheduleTask(15);
scheduleTask(25);
scheduleTask(45);
}
}
public class SomeTask extends TimerTask {
@Override
public void run() {
// do something
}
}
Schedule monthly task using ScheduledExecutorService
If you’re running in a Java EE environment, you should use a TimerService or the @Schedule annotation. But since you’re discussing ScheduledExecutorService, whose use is not permitted in a Java EE container, I’ll assume you are not running in one.
When using a ScheduledExecutorService, you can have the task itself schedule the next iteration:
final ScheduledExecutorService executor = /* ... */ ;
Runnable task = new Runnable() {
@Override
public void run() {
ZonedDateTime now = ZonedDateTime.now();
long delay = now.until(now.plusMonths(1), ChronoUnit.MILLIS);
try {
// ...
} finally {
executor.schedule(this, delay, TimeUnit.MILLISECONDS);
}
}
};
int dayOfMonth = 5;
ZonedDateTime dateTime = ZonedDateTime.now();
if (dateTime.getDayOfMonth() >= dayOfMonth) {
dateTime = dateTime.plusMonths(1);
}
dateTime = dateTime.withDayOfMonth(dayOfMonth);
executor.schedule(task,
ZonedDateTime.now().until(dateTime, ChronoUnit.MILLIS),
TimeUnit.MILLISECONDS);
In versions of Java earlier than 8, you can use a Calendar to do the same thing:
final ScheduledExecutorService executor = /* ... */ ;
Runnable task = new Runnable() {
@Override
public void run() {
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MONTH, 1);
long delay =
calendar.getTimeInMillis() - System.currentTimeMillis();
try {
// ...
} finally {
executor.schedule(this, delay, TimeUnit.MILLISECONDS);
}
}
};
int dayOfMonth = 5;
Calendar calendar = Calendar.getInstance();
if (calendar.get(Calendar.DAY_OF_MONTH) >= dayOfMonth) {
calendar.add(Calendar.MONTH, 1);
}
calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
executor.schedule(task,
calendar.getTimeInMillis() - System.currentTimeMillis(),
TimeUnit.MILLISECONDS);
How do I schedule a task to run once?
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
.
Related Topics
Android-Java- How to Sort a List of Objects by a Certain Value Within the Object
Webview and Cookies on Android
Android: Sending Data >20 Bytes by Ble
How to Add Image in a Textview Text
What Is Shareduserid in Android, and How Is It Used
Opengl Es2 Alpha Test Problems
Gradle - What Is a Non-Zero Exit Value and How to Fix It
Android: How to Stretch an Image to the Screen Width While Maintaining Aspect Ratio
How to Make a Copy of a File in Android
Transitive Dependencies Not Resolved for Aar Library Using Gradle
How to Update a Textview of an Activity from Another Class
How to Find the Currently Running Applications Programmatically in Android
How to Check Certificate Name and Alias in Keystore Files
Android M Permissions: Onrequestpermissionsresult() Not Being Called