用于在Java Spring框架中定时执行特定任务的注解 @Scheduled,它能够指定方法在特定时间间隔或特定时间点执行。默认参数是cron,cron参数被用来定义一个Cron表达式,它代表了任务执行的时间规则
参数如下
Cron
这是是一种时间表达式,用于表示定时任务的执行规则。在Spring中,@Scheduled注解的cron参数就是用来设置Cron表达式的。Cron表达式的基本格式为:
secdond minute hour dayOfMonth month dayOfWeek year
每个字段的含义如下:
- second:秒(0-59)
- minute:分钟(0-59)
- hour:小时(0-23)
- dayOfMonth:月份中的某一天(1-31)
- month:月份(1-12或者 JAN-DEC)
- dayOfWeek:星期中的某一天(1-7或者 SUN-SAT)
- year:年份(留空表示任何年份)
使用*表示匹配任意值,例如,在month字段中表示每个月,而在dayOfWeek字段中表示每一天。除了之外,你还可以使用一些其他符号,比如:
8.-:表示一个范围,比如 1-5 表示1到5。
9.,:表示一个列表,比如 1,3,5 表示1、3和5。
10./:表示间隔,比如 0/15 表示每隔15分钟。
例如,要设置每天上午10点执行任务,Cron表达式可以是 0 0 10 * * ?。 0 0/1 * * * ?,表示每隔一分钟执行一次任务。
示例:
@Scheduled(cron = “0 15 10 * * ?”) // 每天上午10:15执行
fixedRate
这个参数定义了方法调用之间的固定周期,单位为毫秒。不论前一次方法执行花费了多长时间,都会按照这个间隔执行。如果上一个任务堵塞不排除脏数据的风险
示例:
@Scheduled(fixedRate = 1000) // 每1000毫秒执行一次
fixedDelay
这个参数定义了在上一次方法执行完毕后到下一次开始执行的间隔时间,单位也是毫秒。不同于 fixedRate,fixedDelay 会等待前一次方法执行完成后才开始计时。
示例:
@Scheduled(fixedDelay = 1000) // 完成后1000毫秒再次执行
initialDelay
:这个参数用来定义延迟首次执行任务的时间,单位为毫秒。它通常与 fixedRate 或 fixedDelay 结合使用,用来设置启动后延迟执行任务。
示例:
@Scheduled(fixedRate = 1000, initialDelay = 1000) // 启动后延迟1000毫秒,之后每1000毫秒执行一次
zone
:用于指定 cron 表达式的时区,默认是服务器的本地时区。
示例:
@Scheduled(cron = "0 15 10 * * ?", zone = "America/New_York") // 指定时区为纽约
使用 @Scheduled 注解需要在 Spring 配置中启用定时任务(通过注解 @EnableScheduling)。这样,Spring 的任务调度器就会自动识别使用了 @Scheduled 注解的方法,并根据设定的规则执行这些方法。
最后在启动类上开启该方法@EnableScheduling
多个服务导致资源冲突问题
当多个服务同时操作一个任务就会造成数据冲突,所以就需要分布式锁,redis的setnx,无状态,可共享,读取块就能成为很好的解决方案
redisssesion实现分布式锁
也可以只有使用redis 上锁方法
public String tryLock(String name, long expire) {
name = name + "_lock";
String token = UUID.randomUUID().toString();
RedisConnectionFactory factory = stringRedisTemplate.getConnectionFactory();
RedisConnection conn = factory.getConnection();
try {
//参考redis命令:在redis中存入数据 数据明 uuid token 为锁的名字
// 因为这个数据的名字都是name+_locak 所以每次调用加锁方法setnx 只有一个成功调用的才能才能成功的保存的一个锁数据,其他的只能无法设置
//set key value [EX seconds] [PX milliseconds] [NX|XX]
Boolean result = conn.set(
name.getBytes(),
token.getBytes(),
Expiration.from(expire, TimeUnit.MILLISECONDS),
RedisStringCommands.SetOption.SET_IF_ABSENT //NX
);
if (result != null && result)
return token;
} finally {
RedisConnectionUtils.releaseConnection(conn, factory,false);
}
return null;
}
方法调用
因为我的定时任务是每隔1minute 执行一次,所以每次上锁30s,让其他任务线程无法获取,这里是统一名字实际业务开发,一个数据的操作可以使用一个数据的主键+前后缀来作为锁名
@Scheduled(cron = "0 */1 * * * ?")
public void refresh(){
String token = cacheService.tryLock("FUTURE_TASK_SYNC", 1000 * 30);
if(StringUtils.isNotBlank(token)){
//。。。。。执行逻辑
}
}
}
如果这个定时任务是你的服务一启动就开始计时的,那么可以在该方法 添加这初始化注解 @PostConstruct,在启动类一启动伴随ioc初始化