Get next time when TimerTask is scheduled to run - java

So I have a repeating TimerTask that runs every x number of seconds (I say x as it can be changed by the user)
I also need to add a UI feature as "time until next execution" in seconds. I could do something where I store an AtomicInteger which I increment every second, and then do some other checking of that integer for if I want to actually 'run' the task. However I'm ideally looking for a simpler way (for example, if I run the task every second but the actual code should be running every 566ms, it won't work properly). I've taken a look at TimerTask#scheduledExecutionTime, but that returns the last time it's run. Is there an easier way to do this? Thanks

you could use ScheduledThreadPoolExecutor.scheduleAtFixedRate
here is simple example
public static void main(String[] args) throws InterruptedException {
ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(10);
ScheduledFuture<?> task = scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
System.out.println("task");
}, 1, 1, TimeUnit.SECONDS);
scheduledThreadPoolExecutor.scheduleAtFixedRate(() -> {
System.out.println("until " + task.getDelay(TimeUnit.MILLISECONDS));
}, 500, 700, TimeUnit.MILLISECONDS);
Thread.sleep(10000);
scheduledThreadPoolExecutor.shutdownNow();
}

Related

Java8 ScheduledExecutorService.scheduleAtFixedRate()

I'm reading a java8 book and come across the difference between scheduleAtFixedRate and scheduleWithFixedDelay methods from ScheduledExecutorService.
I understand the difference between these two methods on the book, however when I tried to write a simple code. It's not so clear why scheduleAtFixedRate behaves synchronously.
As you can see, I'm allocating 100 threads in the pool. And the scheduler will submit a new task every 1ms, and per task, there's a delay of 1s.
ScheduledExecutorService s = Executors.newScheduledThreadPool(100);
s.scheduleAtFixedRate(() -> {
int num = new Random().nextInt(100);
System.out.println("Hello World" + num);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Finished" + num);
}, 0, 1, TimeUnit.MILLISECONDS);
But why am I getting this output ? A new task will run only after the other.
Hello World94
Finished94
Hello World14
Finished14
Hello World90
Finished90
Hello World26
Finished26
Take a look at the javadoc for ScheduledThreadPoolExecutor#scheduleAtFixedRate
If any execution of this task takes longer than its period, then
subsequent executions may start late, but will not concurrently
execute.
So do not await for it to execute concurrently, it will always execute sequentially..

Java stop scheduled task if it takes more than a specific time

I have a scheduled job which runs every 100 seconds. Sometimes the execution of this method takes a lot of time (which is ok and there is no problem with that). In this situation, the result of the running method is not important to me and I want to re-schedule the job for next 100 second.
What is the best way to force the running job to terminate (return) after a specific time?
My scheduled code is like below:
#Scheduled(fixedDelay = 100*1000)
fun calculateLastDaysStatistics() {
logger.info("affiliate statistics thread Started Successfully")
val processStartDate = Date()
for (i in 1..prevDaysToConsider) {
logger.info("AdZone-Stats prev days $i")
val yesterday = DateUtility.addDay(Date(), -i)
val startDate = DateUtility.getZeroDayTime(yesterday.time)
val endDate = DateUtility.addDay(startDate, 1)
/* This method is probable to take a lot of time */
calculateStatistics(startDate, endDate)
}
val processLength = (Date().time - processStartDate.time) / 1000
logger.info("affiliate statistics thread finished in " + processLength + "s")
}
Thanks.
Try using Fixed Rate instead of Fixed Delay
Here is the article from
Paraschiv.E. The #Scheduled Annotation in Spring. Referred from https://www.baeldung.com/spring-scheduled-tasks
Schedule a Task at a Fixed Rate
#Scheduled(fixedRate = 1000)
public void scheduleFixedRateTask() {
System.out.println(
"Fixed rate task - " + System.currentTimeMillis() / 1000);
}
Note that the beginning of the task execution doesn’t wait for the completion of the previous execution.
This option should be used when each execution of the task is independent.
You can implement a custom Task scheduler using, org.springframework.scheduling.TaskScheduler instead of Annotation based method.
private final TaskScheduler scheduler;
#Autowired
public SchedulingManager(TaskScheduler scheduler) {
this.scheduler = scheduler;
}
In this case,
ScheduledFuture scheduledeFuture = scheduler.schedule(()->{
....You job goes here..
}, new CronTrigger("*/100 * * * * *"));
You can keep track of the scheduled future to make sure it runs max time intended.
scheduledeFuture.get(100,TimeUnit.SECONDS)

Android Java - Runnable confusion

I'm learning about Runnable right now and am a little confused with a code I found and how it's running.
j = 0;
public Runnable test = new Runnable() {
#Override
public void run() {
if (j <= 4) { //this is an if statement. shouldn't it run only once?
Log.i("this is j", "j: " + j);
handler.postDelayed(this, 2000); //delays for 2 secs before moving on
}
j++; //increase j. but then why does this loop back to the top?
Log.i("this is j", "incremented j: " + j);
}
};
When I run this, every 2 seconds j will log from 0 to 4. I don't understand why though, but it does exactly what I need of having a data updated every 2 seconds.
Does run() just keep... running? Which would explain why it keeps looping, kinda. But then if that was the case then even after the if statement finishes j would still be incrementing itself.
Any help in explaining this would help, thanks!
Check out the documentation for Handler.postDelayed(Runnable, long):
Causes the Runnable r to be added to the message queue, to be run after the specified amount of time elapses.
What postDelayed() does is take a Runnable instance and call its run() method after a given delay. It does not resume execution where you left off.
In your case, you are passing this which is a Runnable that checks if (j <=4 )) and if so, posts that same runnable again, thus executing the run() method again.
If you just want a delay after checking if j <= 4, you likely want Thread.sleep() which will sleep a thread for a given amount of time.
A Runnable is just that: a block of code that can be run. The magic happens when you use Runnable's with a Handler, like you are doing here. A Handler will accept Runnable's and call their run() method on the Handler's thread. You tell a Handler to run a Runnable using Hander.post() or Handler.postDelayed(). post() runs the Runnable immediately, postDelayed() runs it after a given amount of milliseconds.
So the run() method is only run once, but this line:
handler.postDelayed(this, 2000);
tells the Handler to schedule running this (that is, this Runnable) after 2000 milliseconds (2 seconds).

Running repeated task from a foreground service

I want to run repeated task 3 times only from a foreground service.
I tried to use ScheduledExecutorService
ScheduledExecutorService scheduler = Executors
.newScheduledThreadPool(1);
counter = 0;
scheduler.scheduleWithFixedDelay(new Runnable() {
public void run() {
if (counter<3) {
counter++;
System.out.println("Do something useful");
}
else {
scheduler.shutdownNow()
}
}
}
}, 2, 2, TimeUnit.SECONDS);
But im not sure if it's the right way to go with.
This repeated task will run only 3 times and will stop after that.
AlarmManager is another option but sounds to me like an overkill for this short period task.
Any advice would be helpful!

What is the difference between schedule and scheduleAtFixedRate?

What is the difference between these 2 methods of Timer class :
schedule(TimerTask task, long delay, long period)
and
scheduleAtFixedRate(TimerTask task, long delay, long period)
Documentation doesn't make the difference between them clear.
The documentation does explain the difference:
schedule:
In fixed-delay execution, each execution is scheduled relative to the actual execution time of the previous execution. If an execution is delayed for any reason (such as garbage collection or other background activity), subsequent executions will be delayed as well.
So, suppose the delay is 5 seconds, and each task takes 2 seconds, you would get
TTWWWTTWWWTTWWWTT
where T means 1 second for the task execution, and W means 1 second waiting.
But now suppose that a long GC (represented by a G) happens and delays the second task, the third one will start 5 seconds after the start of the second one, as if the long GC didn't happen:
TTWWWGGTTWWWTTWWWTT
The third task starts 5 seconds after the second one.
scheduleAtFixedRate:
In fixed-rate execution, each execution is scheduled relative to the scheduled execution time of the initial execution. If an execution is delayed for any reason (such as garbage collection or other background activity), two or more executions will occur in rapid succession to "catch up.".
So, with the same delay as above, and the same GC, you would get
TTWWWGGTTWTTWWWTT
The third task task starts 3 seconds instead of 5 after the second one, to catch up.
Thanks #Nizet's answer, I have written a sample code for some people who want to practice and learn.
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest {
public static void main(String args[]){
TimerTest.DelayTask task = new DelayTask();
Timer timer = new Timer();
/**
* Use schedule or scheduletAtFixedrate and check the printed result
*/
timer.schedule(task, 0, 5000);
//timer.scheduleAtFixedRate(task, 0, 5000);
}
public static boolean stop = false;
public static void delayOneSec(String status){
try{
System.out.print(status);
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
}
static class DelayTask extends TimerTask{
int count = 2;
#Override
public void run() {
// TODO Auto-generated method stub
stop = true;
for(int i = 0; i < count; i++){
TimerTest.delayOneSec("T");
}
if(count == 2){
count = 6;
}else{
count = 2;
}
stop = false;
new PrintW().start();
}
}
static class PrintW extends Thread{
#Override
public void run(){
while(!stop){
TimerTest.delayOneSec("W");
}
}
}
}
The task itself will repeat to take 2 seconds or 6 seconds. Let's see the result of each scenario.
When using timer.schedule(task, 0, 5000);, the output is TTWWWTTTTTTTTWWWTTTTTTTTWWWTTTTTTTT. As you can see, the timer follow the rules like below, wait till period time outs if task finishes in time, launch next task immediately if current task lasts more than period.
When using timer.scheduleAtFixedRate(task, 0, 5000);, the output is TTWWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTT. Things are a little different now. The javadoc
two or more executions will occur in rapid succession to "catch up."
takes effect here. As you can see, ignoring the first TTWWW, every two tasks will print TTTTTTTTWW and it lasts 10 seconds(two periods).
Let's dig into the source code of Timer.
public void schedule(TimerTask task, Date firstTime, long period) {
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, firstTime.getTime(), -period);
}
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, period);
}
As you can see, the period is transferred to negative value in schedule method. Let's see what's the difference when scheduling it.
The below code is in the mainloop of TimerThread,
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
It's where magic happens, for schedule method, the next task execution time is based on the currentTime which is calculated right before the this task runs. That means, every task's execution time only be related with previous task starts time.

Categories

Resources