I want to write a back-ground job (EJB 3.1), which executes every minute. For this I use the following annotation:
@Schedule(minute = "*/1", hour = "*")
which is working fine.
However, sometimes the job may take more than one minute. In this case, the timer is still fired, causing threading-issues.
Is it somehow possible, to terminate the scheduler if the current execution is not completed?
I ran into the same problem but solved it slightly differently.
This works by setting up a task to execute in the future (in this case, in one second). At the end of the task, it schedules the task again.
EDIT: Updated to refactor the "stuff" into another method so that we can guard for exceptions so that the rescheduling of the timer always happens
If only 1 timer may ever be active at the same time, there are a couple of solutions.
First of all the
@Timer
should probably be present on an@Singleton
. In a Singleton methods are by default write-locked, so the container will automatically be locked-out when trying to invoke the timer method while there's still activity in it.The following is basically enough:
atSchedule
is write-locked by default and there can only ever be one thread active in it, including calls initiated by the container.Upon being locked-out, the container may retry the timer though, so to prevent this you'd use a read lock instead and delegate to a second bean (the second bean is needed because EJB 3.1 does not allow upgrading a read lock to a write lock).
The timer bean:
The worker bean:
This will likely still print a noisy exception in the log, so a more verbose but more silently solution is to use an explicit boolean:
The timer bean:
The worker bean:
There are some more variations possible, e.g. you could delegate the busy check to an interceptor, or inject a singleton that only contains the boolean into the timer bean, and check that boolean there, etc.
well I had a similar problem. There was a job that was supposed to run every 30 minutes and sometimes the job was taking more than 30 minutes to complete in this case another instance of job was starting while previous one was not yet finished. I solved it by having a static boolean variable which my job would set to true whenever it started run and then set it back to false whenever it finished. Since its a static variable all instances will see the same copy at all times. You could even synchronize the block when u set and unset the static variable. class myjob{ private static boolean isRunning=false;
Since Java EE 7 it is possible to use an "EE-aware" ManagedScheduledExecutorService, i.e. in WildFly:
In for example a
@Singleton @Startup @LocalBean
, inject the default "managed-scheduled-executor-service" configured instandalone.xml
:Schedule some task in
@PostConstruct
to be executed i.e. every second with fixed delay:scheduleWithFixedDelay:
Do not shutdown the scheduler in i.e.
@PreDestroy
: