Run scheduled jobs in Java
Compare Quartz cron triggers, Spring @Scheduled, and ScheduledExecutorService, including persistence and overlapping execution.
Use Quartz when you need cron expressions and features such as persistent job definitions or clustered scheduling. For straightforward tasks in a Spring application, @Scheduled is often sufficient. For a fixed interval with no calendar rules, the JDK's scheduled executor needs no extra dependency.
Option 1: Quartz Scheduler
Add the dependency
This example uses the Quartz 2.3.x API and pins the original example's version; it is not a claim that 2.3.2 is the latest release. Choose a supported version compatible with your JDK and dependency policy.
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
Define the job
Save this as HelloJob.java:
import java.time.LocalDateTime;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) {
System.out.println("Hello world! " + LocalDateTime.now());
}
}
Register a cron trigger
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
public class SchedulerMain {
public static void main(String[] args) throws SchedulerException {
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
JobDetail job = JobBuilder.newJob(HelloJob.class)
.withIdentity("helloJob", "group1")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("helloTrigger", "group1")
.withSchedule(CronScheduleBuilder.cronSchedule("0 * * * * ?"))
.build();
scheduler.scheduleJob(job, trigger);
}
}
Quartz expressions have six required fields and an optional year: seconds, minutes, hours, day of month, month, day of week, and optionally year. 0 * * * * ? runs at second zero of every minute. Linux crontab's usual five-field expression is different.
Quartz can use a JDBC JobStore, coordinate a cluster, and apply misfire policies. Configure those features deliberately; this minimal example does not enable durable or clustered execution. Integrate scheduler shutdown with the application's lifecycle.
Option 2: Spring @Scheduled
Enable scheduling with @EnableScheduling, then register a Spring-managed component:
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class ReportTask {
@Scheduled(cron = "0 30 2 * * ?")
public void dailyReport() {
// Generate the report.
}
}
Spring's cron format also has six fields, starting with seconds, but it is not identical to Quartz in every detail. Set the zone attribute when the business time zone must be explicit.
For a fixed rate, @Scheduled(fixedRate = 300_000) uses a five-minute interval in milliseconds.
Option 3: ScheduledExecutorService
For interval-based work:
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
executor.scheduleAtFixedRate(() -> doWork(), 0, 5, TimeUnit.MINUTES);
Import these types from java.util.concurrent and supply doWork(). Shut down the executor during application shutdown. An exception escaping a periodic execution suppresses later executions of that task, so handle and report expected failures deliberately.
For any of these options, log the task name, start time, completion result, and duration. Collect those records with DataKit to distinguish a missed start, a failed execution, and a task still running. Scheduler-startup logs alone do not establish job success.
FAQ
What does Quartz's ? mean?
It means no specific value in the day-of-month or day-of-week field. For example, 0 30 2 ? * MON specifies Monday and leaves day of month unspecified. Quartz does not support specifying both day fields together in the usual way.
Does fixedRate allow a task to overlap itself?
A fixed rate is measured against scheduled start times; a fixed delay is measured from the previous completion. The JDK scheduled executor does not overlap successive executions of the same periodic task: a slow execution delays later runs. Spring behavior depends on the scheduler and execution model. Asynchronous dispatch, multiple schedules, or multiple application instances can still overlap. Do not infer concurrency from the annotation name alone.
Will several application instances run the same @Scheduled task?
Yes, if each registers the task. @Scheduled does not coordinate the cluster. Use a correctly configured Quartz JDBC cluster or a suitable distributed lock, and still make business effects idempotent.
References: Spring scheduling, JDK scheduled executor.
Contact us
Join the community
to join the community
Try Guance
Start online and pay only for what you use.
Get startedChoose a Guance plan