Java案例如何定时同步数据?

wen java案例 70

本文目录导读:

Java案例如何定时同步数据?

  1. 基础方案:使用 java.util.Timer (适合简单任务)
  2. 企业级方案:Spring @Scheduled (最常用)
  3. 分布式方案:使用 Quartz (适合集群、复杂调度)
  4. 轻量级方案:使用 Executors + ScheduledExecutorService
  5. 最佳实践建议

在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();
    }
}

最佳实践建议

  1. 使用 @Scheduled 替代 Timer:除非项目真的不用Spring。

  2. 始终添加异常处理:定时任务中抛出异常会导致任务永久停止(对 @ScheduledTimer 都是)。

  3. 考虑任务重叠问题

    • 如果同步耗时可能超过间隔时间,用 fixedDelay 而不是 fixedRate
    • 或者使用 @Async + @Scheduled 让任务异步执行
  4. 分布式场景使用 Quartz 或 xxl-job:避免多台服务器同时执行。

  5. 动态配置:将 cron 表达式放配置文件或数据库,避免改代码。

# application.yml
task:
  sync:
    cron: "0 0 2 * * ?"
@Scheduled(cron = "${task.sync.cron}")
public void dynamicSync() {
    // ...
}
  1. 加分布式锁:如果不想用 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 分布式锁。

抱歉,评论功能暂时关闭!