I'm trying to execute a task at midnight after a fixed amount of days using ScheduleExecutorService. My method runs inside of my tomcat 8 and looks like this:
public void schedule (int aInterval)
{
String timezone = TimeZone.getDefault().getID();
ZoneId z = ZoneId.of(timezone);
ZonedDateTime now = ZonedDateTime.now( z );
LocalDate tomorrow = now.toLocalDate().plusDays(1);
ZonedDateTime tomorrowStart = tomorrow.atStartOfDay( z );
Duration duration = Duration.between( now , tomorrowStart );
long millisecondsUntilTomorrow = duration.toMillis();
long interval;
if (aInterval * 24 * 60 * 60 > Long.MAX_VALUE)
{
interval = Long.MAX_VALUE;
}
else
{
interval = aInterval * 24 * 60 * 60;
}
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
// allow the JVM to kill the scheduled task
thread.setDaemon(true);
return thread;
});
scheduler.scheduleAtFixedRate(new Runnable()
{
public void run() {
System.out.println(String.format("schedule::run() at %1$Td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS \n", System.currentTimeMillis() ) );
doTask();
}
},
delay,
interval,
TimeUnit.SECONDS);
}
Now when the method is first run it seems like is not executed as specifed by delay and interval. E.g. when i set delay=60 and interval=5 in my stacktrace it looks like this:
...
schedule::run() at 10.08.2017 17:57:09
schedule::run() at 10.08.2017 17:57:15
schedule::run() at 10.08.2017 17:57:21
schedule::run() at 10.08.2017 17:57:27
schedule::run() at 10.08.2017 17:57:27
schedule::run() at 10.08.2017 17:57:33
schedule::run() at 10.08.2017 17:57:33
schedule::run() at 10.08.2017 17:57:34
...
So the intervals somehow are becoming shorter and shorter over time. What is going on here? Is there a problem in my method?
Now when the method is first run it seems like is not executed as specified by delay and interval. E.g. when i set delay=60 and interval=5 in my stacktrace it looks like this...
I try to be very explicit with my time variables. In this case, you should be dealing with milliseconds so delayMillis, intervalMillis, aIntervalMillis, etc. should be in your code. If they are seconds then use delaySecs, etc. but you will need to multiple them by 1000 when you pass them to the scheduleAtFixedRate(...) method which is expecting millis.
So the intervals somehow are becoming shorter and shorter over time. What is going on here? Is there a problem in my method?
What's probably going on is that the task is trying to schedule it every 5 milliseconds so the random delays are just showing you how long your doTask() takes to run. If you want to set them to 60 and 5 seconds respectively then you should use 60000 and 5000 instead.
When i try that and my schedule() method is running i get a message from my eclipse (neon 3) saying tomcat is not responding and my tomcat is not shutting down properly.
Not sure about this but I suspect that your tomcat is waiting for your scheduled task to finish which is never will on its own. You could use a ThreadFactory and create a daemon thread instead which the JVM will not wait for when it shuts down. Note that if it is a daemon thread then it might get terminated right in the middle of running.
scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
// allow the JVM to kill the scheduled task
thread.setDaemon(true);
return thread;
}
});
Also, you should call scheduler.shutdown() on your thread-pool after you submit your fixed task. It will continue to run but no other jobs will be submitted. That's a good pattern.
How can I stop my execution task in a proper way?
The daemon thread mode about may be "proper" in your case but you can also cancel it. The scheduler.scheduleAtFixedRate(...) method returns a ScheduledFuture object that you can call cancel(...) on to stop it. Once your doTask() finishes it won't be scheduled again. You can also call cancel(true) on it to set the interrupt flag on the thread but your code is going to have to handle that specifically.
Of course you are going to need to detect that your JVM is shutting down so you can cancel your task. You could set a shutdown hook which is a bit of a hack. Maybe there is some way for tomcat to notify that it's coming down?
the intervals somehow are becoming shorter and shorter over time
We need to understand how ScheduledExecutorService:scheduleAtFixedRate is supposed to work. According to docs, https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html#scheduleAtFixedRate, we can conclude the following:
The time of each run is pre-determined.
This pre-calculated time may not tally when the previous run of the same task takes longer than point no 1. In such case, the
next run will happen immediately after the longer running previous run
has finished.
Same task can never run concurrently; even if the expected start time of the next run has exceeded. As mentioned by point no 2, the next run will wait till
the previous longer run finishes.
Now, coming to your case, you had expected to see your task getting run at equal intervals. But, instead the intervals between them are getting shorter with each run. This could be due to any previous run that took considerable longer time, piling up other run(s) which have exceeded their expected start time. Further piled up runs (doTask method) finish quickly. So, all the piled up runs are running at closer intervals. If you really want the task to run at equal intervals, you could instead use ScheduledExecutorService: scheduleWithFixedDelay
Related
I am using Java executorservice to create a timeout effect in one of my apps. After an elapsed time, they executor service begins and logs the user out of their session. But on an Android device when the device goes to sleep the executor thread is suspended. After the device awakes the thread is unsuspended. I would like the change the clock the executor is using so that it continues counting even after the device goes to deep sleep. Is there no way I can over ride which clock is being used (I realize I can swap out the entire implementation and use alarmmanager but I'm looking to not alter existing code at this point so please do not try to offer other APIs).
My question is, there must be a system clock that keeps going despite the device being asleep, how can I let the executor scheduler use that clock instead of the one it's using now which respects the device going to deep sleep and pauses ticking?
My code I have currently is very simple and just looks like this:
myExecutorService.schedule(new EndUserSession(),
6L, TimeUnit.MINUTES);
this code above starts the EndUserSession() in 6 minutes. It works but I want to use another clock that does not respect time out of mobile device.
I have strong doubts that it's possible to influence scheduler service timing mechanisms.
You have another option to avoid problems caused by device sleep, like passing specific timestamp in constructor and scheduling a task at fixed rate. Example:
class EndSessionTask {
final long sessionExpirationTime;
volatile ScheduledFuture future;
public EndSessionTask(long ts) { sessionExpirationTime = ts; }
public void run() {
if (sessionExpirationTime < currentTs) return;
endSession();
future.cancel();
}
public void setFuture(ScheduledFuture f) { this.future = f; }
}
long endSessionTime = System.currentTimeMillis() + 6 * 60 * 1000;
EndSessionTask task = new EndSessionTask(endSessionTime);
ScheduledFuture future = executorService.scheduleAtFixedRate(task, 10L, 10L, SECONDS);
task.setFuture(future);
I have a problem, I am trying to execute a Task in ScheduledExecutorService, and I am executing the task with the command:
updateTagDataHandle = scheduler.scheduleWithFixedDelay(updateTagDataRunnable, 500, 500, TimeUnit.MILLISECONDS);
and after several success runs it stops. the task itself takes a few seconds I checked with println that it go to the end of the task with no errors, and I print any exception and didnt see an exception in the end. I need it to continue run infinite number of times.
any help would be appreciated
edit
my code for initializing the task scheduler was:
scheduler = Executors.newScheduledThreadPool(1);
so the corePoolSize = 1 so there only one thread alive and the task share this on thread. but setting the threadPool to be more than one is not helping and still there seems to be only one thread active.
same question here:
Executors Factory method newScheduledThreadPool always returns the same Thread pool
and here:
Why doesn't ScheduledExecutorService spawn threads as needed?
any help would be appreciated
edit:
didnt find a solution so used the scheduler custom thread creation :
scheduler = Executors.newScheduledThreadPool(7, new ThreadFactory() {
#Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
});
Try changing the initial delay to 1000 milliseconds.
I was trying the same code in my android app and this solved the problem.
What's the main difference between scheduleAtFixedRate and scheduleWithFixedDelay methods of ScheduledExecutorService?
scheduler.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
System.out.println("scheduleAtFixedRate: " + new Date());
}
}, 1, 3L , SECONDS);
scheduler.scheduleWithFixedDelay(new Runnable() {
#Override
public void run() {
System.out.println("scheduleWithFixedDelay: " + new Date());
}
}, 1, 3L , SECONDS);
they print exact the same time, seems they are executed at exact the same interval.
Try adding a Thread.sleep(1000); call within your run() method... Basically it's the difference between scheduling something based on when the previous execution ends and when it (logically) starts.
For example, suppose I schedule an alarm to go off with a fixed rate of once an hour, and every time it goes off, I have a cup of coffee, which takes 10 minutes. Suppose that starts at midnight, I'd have:
00:00: Start making coffee
00:10: Finish making coffee
01:00: Start making coffee
01:10: Finish making coffee
02:00: Start making coffee
02:10: Finish making coffee
If I schedule with a fixed delay of one hour, I'd have:
00:00: Start making coffee
00:10: Finish making coffee
01:10: Start making coffee
01:20: Finish making coffee
02:20: Start making coffee
02:30: Finish making coffee
Which one you want depends on your task.
Visualize time series of invocation scheduleAtFixedRate method. Next executions will start immediately if the last one takes longer than period. Otherwise, it will start after period time.
Time series of invocation scheduleWithFixedDelay method. Next execution will start after delay time between termination of one execution and the commencement of the next, regardless of its execution time
Hope can help you
The scheduleAtFixedRate() method creates a new task and submits it to the executor every period, regardless of whether or not the previous task finished.
On the other hand, the scheduleWithFixedDelay() method creates a new task after the previous task has finished.
If you read the Java Doc it will be clearer
ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; that is executions will commence after initialDelay then initialDelay+period, then initialDelay + 2 * period, and so on.
ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given delay between the termination of one execution and the commencement of the next.
There is one catch in scheduleAtFixedRate if first thread is taking too long and not ended in given duration then second conscutive thread will not start once the first task will get finsished and will not imediately get started while the first thread has comepleted their task and gievn duration has been elapsed. JVM Will decide when the next task will get executed .
I think that will help you to choose method Becuase due to this i got big problem
I can see your premiss but your conclusion is not quite right.
Here is a good and quite complete explanation according to this Tutorial for understanding the diferences bitween these two.
scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)
This method schedules a task to be executed periodically. The task is executed the first time after the initialDelay, and then recurringly every time the period expires.
If any execution of the given task throws an exception, the task is no longer executed. If no exceptions are thrown, the task will continue to be executed until the ScheduledExecutorService is shut down.
If a task takes longer to execute than the period between its scheduled executions, the next execution will start after the current execution finishes. The scheduled task will not be executed by more than one thread at a time.
scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)
This method works very much like scheduleAtFixedRate() except that the period is interpreted differently.
In the scheduleAtFixedRate() method the period is interpreted as a delay between the start of the previous execution, until the start of the next execution.
In this method, however, the period is interpreted as the delay between the end of the previous execution, until the start of the next. The delay is thus between finished executions, not between the beginning of executions.
Let's write a simple program:
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
var time = 0L
var start = System.currentTimeMillis()
val executor = Executors.newScheduledThreadPool(1)
executor.scheduleWithFixedDelay({
if (time >= 12_000L) {
executor.shutdown()
} else {
Thread.sleep(2000L)
val now = System.currentTimeMillis()
time += now - start
System.out.println("Total $time delay ${now - start}\n")
start = now
}
}, 0L, 1000L, TimeUnit.MILLISECONDS)
And see the results:
| scheduleWithFixedDelay | scheduleAtFixedRate |
|:----------------------:|:----------------------:|
| Total 2001 delay 2001 | Total 2003 delay 2003 |
| Total 5002 delay 3001 | Total 4004 delay 2001 |
| Total 8003 delay 3001 | Total 6004 delay 2000 |
| Total 11003 delay 3000 | Total 8004 delay 2000 |
| Total 14003 delay 3000 | Total 10005 delay 2001 |
| --- | Total 12005 delay 2000 |
NOTICE the execution time is bigger than waiting
scheduleWithFixedDelay keeps delay
scheduleAtFixedRate removes delay
scheduledExecutorService.scheduleAtFixedRate(() -> {
System.out.println("runnable start"); try { Thread.sleep(5000); System.out.println("runnable end");} catch
(InterruptedException e) { // TODO Auto-generated catch block
e.printStackTrace(); }}, 2, 7, TimeUnit.SECONDS);
scheduledExecutorService.scheduleWithFixedDelay(() -> {
System.out.println("runnable start"); try { Thread.sleep(5000); System.out.println("runnable end");} catch
(InterruptedException e) { // TODO Auto-generated catch block
e.printStackTrace(); } }, 2, 7, TimeUnit.SECONDS);
Just execute it, and you will know the difference. Thank you
I have a simple task: I need to run a process on the first day every 2 months even the server was down
If the server will not down – the task is very easy:
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
CronTrigger trigger = new CronTrigger("0 0 1 1 */2 ?");
scheduler.schedule(new Runnable() {
#Override
public void run() {
// do the job
job();
}
}, trigger);
But what if the server was down and I run my job a month ago?
In this case I want to execute my job on the beginning of the next month.
I decided to store in the database when I executed the job last time:
private void job() {
// Store when the job was executed the last time
}
Now, when my server is launched, I need to start the trigger once again, but not immediately.
I can easily calculate when I want to start the trigger, but unfortunately I can not find the appropriate function in ThreadPoolTaskScheduler. There is function that will allow to run task at start time periodically:
public ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period)
Unfortunately, ThreadPoolTaskScheduler does not support
public ScheduledFuture schedule(Runnable task, Date startTime, Trigger trigger)
I implemented the functionality using additional scheduler.execute, but the question if it possible to do it using one schedule.
You could use java.util.Timer to do that, and schdule the next TimerTask inside your TimerTask implementation.
If it has to be exactly two months you should schedule another TimerTask when the TimerTask starts, otherwise you could schedule it when the TimerTask ends.
I have tried many different ways to immediately stop a task which is started using an ExecutorService, with no luck.
Future<Void> future = executorService.submit(new Callable<Void>(
public Void call () {
... do many other things here..
if(Thread.currentThread.isInterrupted()) {
return null;
}
... do many other things here..
if(Thread.currentThread.isInterrupted()) {
return null;
}
}
));
if(flag) { // may be true and directly cancel the task
future.cancel(true);
}
Sometimes I need to cancel the task immediately after it is started, you may be curious why I want to do this, well you may imagine a senario that a user accidentally hits the "Download" button to start a "Download Task" and he immediately wants to cancel the action because it was just an accidental click.
The problem is that after calling future.cancel(true), the task is not stopped and Thread.currentThread.isInterrupted() still returns false and I have no way to know the task was stopped from inside the call() method.
I am thinking of setting a flag like cancelled=true after calling future.cancel(true) and checking that flag constantly in the call() method, I think this is a hack and the code could be very ugly because the user can start many tasks at the same moment.
Is there a more elegant way of achieving what I want?
EDIT:
This really drives me mad. I have spent almost a day on this problem now. I will try to explain a little bit more for the problem I am facing.
I do the following to start 5 tasks, each task will start 5 threads to download a file. and then I stop all 5 tasks immediately. For all of the method calls below, i start a thread(ExecutorService.submit(task)) to make it asynchronous as you can tell from the suffixes of the methods.
int t1 = startTaskAysnc(task1);
int t2 = startTaskAysnc(task2);
int t3 = startTaskAysnc(task3);
int t4 = startTaskAysnc(task4);
int t5 = startTaskAysnc(task5);
int stopTaskAysnc(t1);
int stopTaskAysnc(t2);
int stopTaskAysnc(t3);
int stopTaskAysnc(t4);
int stopTaskAysnc(t5);
in startTaskAysnc(), I simply initiate a socket connection to remote server to get the size of the file(and this certainly is gonna take some time), after successfully getting the fileSize, I will start 5 threads to download different parts of the file. like the following(the code is simplified to make it more easy to follow):
public void startTaskAsync(DownloadTask task) {
Future<Void> future = executorService.submit(new Callable<Void>(
public Void call () {
// this is a synchronous call
int fileSize = getFileSize();
System.out.println(Thread.currentThread.isInterrupted());
....
Future<Void> futures = new Future<Void>[5];
for (int i = 0; i < futures.length; ++i) {
futures[i] = executorService.submit(new Callable<Void>(){...});
}
for (int i = 0; i < futures.length; ++i) {
futures[i].get(); // wait for it to complete
}
}
));
synchronized (mTaskMap) {
mTaskMap.put(task.getId(), future);
}
}
public void stopTaskAysnc(int taskId) {
executorService.execute(new Runnable(){
Future<Void> future = mTaskMap.get(taskId);
future.cancel(true);
});
}
I noticed a weird behavior that after I called stopTaskAsync() for all 5 tasks, there would always be at least one task that got stopped(i.e. Thread.currentThread.isInterrupted() return true), and the other 4 tasks kept running.
And I have tried your suggestions by setting an UncaughtExceptionHandler, but nothing comes out from that.
EDIT:
The problem was solved in this link: Can't stop a task which is started using ExecutorService
Well, the javadoc of Future.cancel(boolean) says that:
If the task has already started, then the mayInterruptIfRunning
parameter determines whether the thread executing this task should be
interrupted in an attempt to stop the task.
so it's quite certain that the thread that executes the task is interrupted. What could have happened is that one of the
... do many other things here..
is accidentally clearing the Thread's interrupted status without performing the desired
handling. If you'll put a breakpoint in Thread.interrupt() you might catch the criminal.
Another option I can think of is that the task terminates before capturing the interrupt, either because it's completed or thrown some uncaught exception. Call Future.get() to determine that. Anyway, as asdasd mentioned, it is a good practice to set an UncaughtExceptionHandler.
What you're doing is very dangerous: you're using a thread pool to execute tasks (which I'll call downloaders), and the same thread pool to execute tasks which
wait for the downloaders to finish (which I'll call controllers)
or ask the controllers to stop
This means that if the core number of threads is reached after the controller has started, the downloaders will be put in the queue of the thread pool, and the controller thread will never finish. Similarly, if the core number of threads is reached when you execute the cancelling task, this cancelling task will be put in the queue, and won't execute until some other task is finished.
You should probably use a thread pool for downloaders, another one for controllers, and the current thread to cancel the controllers.
I think you'll find solution here. The main point is that cancel method raises InterruptedException. Please check if your thread is still running after cancellation? Are you sure that you didn't try to interrupt finished thread? Are you sure that your thread didn't fail with any other Exception? Try to set up UncaughtExceptionHandler.