本文目录导读:

- 基础方案:使用
java.util.Timer(适合简单任务) - 企业级方案:Spring @Scheduled (最常用)
- 分布式方案:使用 Quartz (适合集群、复杂调度)
- 轻量级方案:使用 Executors + ScheduledExecutorService
- 最佳实践建议
在Java中定时同步数据,最常用的方法有几种,具体选择取决于你的项目架构和复杂程度,下面我会从最简单的到最通用的方案逐一介绍,并附上代码案例。
基础方案:使用 java.util.Timer (适合简单任务)
最原始的方式,适合不需要分布式、任务简单的场景。
import java.util.Timer;
import java.util.TimerTask;
public class TimerSyncExample {
public static void main(String[] args) {
Timer timer = new Timer("数据同步定时器");
// 延迟5秒后开始执行,之后每60秒执行一次
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("开始同步数据... 时间:" + System.currentTimeMillis());
// 在这里调用你的同步逻辑
syncData();
System.out.println("同步完成.");
}
}, 5000, 60000); // 5秒后开始,每60秒执行一次
}
public static void syncData() {
// 你的业务逻辑,
// List<User> users = userMapper.selectList();
// targetService.syncUsers(users);
System.out.println("执行数据同步...");
}
}
优点:够简单,无需额外依赖。
缺点:单线程,任务执行时间长会阻塞下次执行;系统重启失效;不适合分布式。
企业级方案:Spring @Scheduled (最常用)
如果你的项目使用Spring Boot,这是最推荐的方案,它使用线程池执行,配置灵活。
开启定时任务支持
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling // 开启定时任务
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
创建定时同步服务
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class DataSyncTask {
/**
* 使用 cron 表达式: 每天凌晨2点执行
* cron = "秒 分 时 日 月 周"
*/
@Scheduled(cron = "0 0 2 * * ?")
public void syncDataDaily() {
System.out.println("每日数据同步开始... 时间:" + System.currentTimeMillis());
performSync();
}
/**
* 固定频率:每30秒执行一次
* 注意:fixedRate 不管上一次是否执行完,到时间就执行下一次
*/
@Scheduled(fixedRate = 30_000)
public void syncDataFixedRate() {
System.out.println("固定频率同步...");
performSync();
}
/**
* 固定延迟:上一次执行完后延迟10秒再执行
* 适合同步时间不确定,需要保证不重叠的情况
*/
@Scheduled(fixedDelay = 10_000)
public void syncDataFixedDelay() {
System.out.println("固定延迟同步...");
performSync();
}
private void performSync() {
// 你的数据同步逻辑
// 从A库查数据,处理后写入B库
// 注意处理异常,避免任务中断
try {
// 业务代码
System.out.println(">> 正在同步数据...");
} catch (Exception e) {
// 记录错误日志,不要吞掉异常
System.err.println("数据同步出错:" + e.getMessage());
}
}
}
常用 Cron 表达式示例
| 表达式 | 含义 |
|---|---|
0 0 2 * * ? |
每天凌晨2点 |
0 0/5 * * * ? |
每5分钟执行一次 |
0 0 1 1 * ? |
每月1号凌晨1点 |
0 0 8-18/2 * * ? |
每天8点到18点,每2小时 |
分布式方案:使用 Quartz (适合集群、复杂调度)
当需要分布式定时任务(多台机器不重复执行)、持久化任务、动态调整时,使用 Quartz。
Spring Boot + Quartz 示例
添加依赖
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
创建 Job 类(任务逻辑)
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
public class DataSyncJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
System.out.println("Quartz 正在同步数据... 当前线程:" + Thread.currentThread().getName());
// 注入需要的 Service(可以通过上下文获取)
// 实际项目中建议通过 ApplicationContext 获取 Bean
try {
// 执行同步逻辑
System.out.println("数据同步完成");
} catch (Exception e) {
throw new JobExecutionException("同步失败", e);
}
}
}
配置 Quartz 调度器
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QuartzConfig {
@Bean
public JobDetail dataSyncJobDetail() {
return JobBuilder.newJob(DataSyncJob.class)
.withIdentity("dataSyncJob")
.storeDurably() // 即使没有 Trigger 关联,也保留 JobDetail
.build();
}
@Bean
public Trigger dataSyncTrigger() {
// 每天凌晨2点执行
CronScheduleBuilder cronSchedule = CronScheduleBuilder.cronSchedule("0 0 2 * * ?");
return TriggerBuilder.newTrigger()
.forJob(dataSyncJobDetail())
.withIdentity("dataSyncTrigger")
.withSchedule(cronSchedule)
.build();
}
}
Quartz 优势:
- 支持集群模式(需要数据库表存储任务状态)
- 支持持久化(机器重启后任务不丢失)
- 支持动态添加、删除、暂停任务
轻量级方案:使用 Executors + ScheduledExecutorService
如果不想引入 Spring,但需要比 Timer 更好的线程池支持:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledExecutorExample {
public static void main(String[] args) {
// 创建一个核心线程数为2的定时任务线程池
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// 延迟5秒后,每10秒执行一次
scheduler.scheduleAtFixedRate(() -> {
System.out.println("数据同步任务开始...");
try {
// 你的业务逻辑
Thread.sleep(2000); // 模拟耗时操作
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("数据同步任务结束...");
}, 5, 10, TimeUnit.SECONDS);
// 注意:程序不会自动关闭,可根据需要调用 scheduler.shutdown()
// 或者让主线程 sleep,演示效果
try {
Thread.sleep(30000); // 运行30秒
} catch (InterruptedException e) {
e.printStackTrace();
}
scheduler.shutdown();
}
}
最佳实践建议
-
使用
@Scheduled替代Timer:除非项目真的不用Spring。 -
始终添加异常处理:定时任务中抛出异常会导致任务永久停止(对
@Scheduled和Timer都是)。 -
考虑任务重叠问题:
- 如果同步耗时可能超过间隔时间,用
fixedDelay而不是fixedRate - 或者使用
@Async+@Scheduled让任务异步执行
- 如果同步耗时可能超过间隔时间,用
-
分布式场景使用 Quartz 或 xxl-job:避免多台服务器同时执行。
-
动态配置:将 cron 表达式放配置文件或数据库,避免改代码。
# application.yml
task:
sync:
cron: "0 0 2 * * ?"
@Scheduled(cron = "${task.sync.cron}")
public void dynamicSync() {
// ...
}
- 加分布式锁:如果不想用 Quartz 又想防止重复执行:
// 使用 Redis 分布式锁
@Scheduled(cron = "0 0 2 * * ?")
public void syncWithLock() {
String lockKey = "data:sync:lock";
Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 30, TimeUnit.MINUTES);
if (Boolean.TRUE.equals(locked)) {
try {
performSync();
} finally {
redisTemplate.delete(lockKey);
}
}
}
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| Timer | 极简、非Spring项目 | 简单直接 | 功能弱、单线程 |
| @Scheduled | 标准Spring Boot项目 | 常用、方便 | 不适合分布式 |
| Quartz | 分布式、复杂调度 | 功能强大、持久化 | 配置稍复杂 |
| ScheduledExecutor | 非Spring但需要线程池 | 灵活 | 手动管理生命周期 |
对于大多数 Java 开发者,首选 Spring 的 @Scheduled 注解。 如果是分布式系统,建议使用 xxl-job(轻量级)或 Quartz(重量级)加上 Redis 分布式锁。