This has been there for a while but I find more people not knowing it exists than those who do. When thinking about scheduling in java most people think Quartz immediately.
Quartz is an amazing library indeed that I find my self bringing to almost every other application that I work on due to its ability to bring cron awesomness to my app mainly and because the idea of the job store is just so darn good when you get used to it. Most of what these people require however are much more simpler and is not really worth the overhead of bringing another dependency in, most people simply need to execute some logic every X units of time; and usually when they bring Quartz and after a while this scheduled logic starts taking more time than the interval between job triggers, they start realizing the fact that quartz will fire another instance of the job and you will be having multiple instances executing the same logic at the same time. So they scour the usual information sources and arrive at the conclusion that they need to have their logic in a StatefulJob.
Well if this all what you need you could use a hidden gem first introduced in 1.5, please all join me in welcoming today’s guest of honor the esteemed
java.util.concurrent
The package includes in its own package description words “Utility classes commonly useful in concurrent programming”. I will only be addressing the scenario described above.
- Grab a scheduled thread pool executer. The easiest way to do this is:
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Please adjust the size of the pool according to the number of jobs you need, if you have a single scheduled job then a single thread is sufficient, add jobs more than the number of pooled threads will have them wait till another thread is available for execution.
- Implement your logic in the form of a runnable
Runnable
, you should account for the following while doing so:
- If the job fails with an exception it will be suppressed by the scheduler, i.e. it will not trigger again until you reschedule it, whether you like this behavior or not (just prefer to swallow whatever exception the job might throw, log it and keep the schedule running) MAKE SOME NOISE WHEN YOUR JOB FAILS, send an email, write in a special log, show it on a dashboard… etc
- If you have some external dependency that might block your logic, notice that no further executions will occur if you get trapped in one since we are emulating a stateful job, always set timeout parameters on external connections if you will use any, and again make as much noise as you can if you get to seem them in use.
- finally to emulate a StatefulJob just invoke:
scheduler.scheduleWithFixedDelay([your-runnable-here], [initial-delay], [delay-between-executions], [time-unit-of-delay-parameters]);
For example if we invoke the method with the params 2,1,TimeUnit.MINUTES
We get our first execution after 2 minutes of the call with a single minute delay between each following execution, the delay is measured after the termination of a previous execution.
Assuming your job executes in 5 minutes we get the executions at:
2, 8, 14, 20, …
(initialDelay + (delay+job_duration)*n) where n = 0,1,2, ..
