定时任务—Spring定时任务

简介

1、各种定时任务

Java集中式定时任务有很多版本:

  • while(true) + Thread.sleep

  • java.util.Timer + java.util.TimerTask

  • ScheduleExecutorService

  • Quartz:可与 Spring整合

  • Spring Task

  • Spring Boot注解:@EnableScheduling + @Scheduled,底层是 Spring Task

2、Spring定时任务

某次定时任务若出现异常,不会影响下次定时任务的执行

这个是是单线程的,假如某一个任务执行时间过长,可能导致别的执行任务到了下一个时间节点,可能导致定时任务拖延或丢失。

2.1、简介

Spring 为我们提供了异步执行任务调度的方式,提供了 TaskExecutor、TaskScheduler接口,还有两个注解 @EnableScheduling、@Scheduled。

2.2、添加注解进行使用

在启动类上添加注解 @EnableScheduling,开启定时任务。

在方法上添加注解 @Scheduled(fixedRate = 10006030),时间单位是毫秒,

该方法不需要被放在 mian函数里面也会被执行,

#

3、Spring双机定时任务

3.1、单机异步多线程

  1. 开启异步。在启动类上加上注解 @EnableAsync。

  2. 在方法上添加注解 @Async,每次触发定时任务都会新开启一个线程。

3.2、简易模拟单机下双机定时任务

3.2.1、思路

两个任务,同时只能有一个在执行,当某一个出现异常的时候,另一个顶替上,所以单机里面用的是加锁来模拟。

3.2.2、实现

private volatile boolean flag = true;
int count = 0;
​
@Scheduled(fixedRate = 1000 * 5)
@Async
public void remindDrink() throws InterruptedException {
  // 执行两次后出现异常
  if (count++ < 2) {
    synchronized (Demo1Application.class) {
      if (flag) {
        flag = false;
        System.out.println("该喝水了!--线程" + Thread.currentThread().getName() + "--" + DateFormat.getDateTimeInstance().format(new Date()));
        flag = true;
      }
    }
  } else {
    flag = false;
  }
}
​
@Scheduled(fixedRate = 1000 * 5)
@Async
public void remindWalk() throws InterruptedException {
    if(flag){
        TimeUnit.SECONDS.sleep(1);
    }
    synchronized (Demo1Application.class) {
        if (!flag) {
            System.out.println("该走动了!--线程" + Thread.currentThread().getName() + "--" + DateFormat.getDateTimeInstance().format(new Date()));
        }
    }
}

3.3、通过中间件 Redis 设定双机定时任务

3.3.1、思路

因为是双机,那么就不能用单机下的锁了,那么就可思考,是不是可以把这个锁放到外面去,例如 redis,

当主机 1做任务的时候,就把 任务名称(key):本机信息(value) 放进去,然后设置一个大于定时任务周期的时间作为其过期时间 TTL,

那么当主机1 在的时候就能一直做任务,每次做任务的时候就再次 set 进去,那么过期时间就会被刷新,它挂了的话,主机2 是一直在尝试做任务的,但是因为 redis 这个 key 一直在所以没做,当 key 过期了不存在,就可以开始自己的任务了。

3.3.2、实现

记得添加对 redisTemplate 的 config,不然会出现乱码。

@EnableAsync
@EnableScheduling
@SpringBootApplication
public class Demo2Application {
  @Autowired
  private RedisTemplate redisTemplate;
  @Value("${server.port}")
  private String port;
  private int count = 0;
  private String taskName = "drink";
  public static void main(String[] args) {
    SpringApplication.run(Demo2Application.class, args);
  }
  
  @Scheduled(fixedRate = 1000 * 5)
  @Async
  public void remindDrink() {
    if (redisTemplate.hasKey(taskName) && !port.equals(redisTemplate.opsForValue().get(taskName))) {
      return;
    }
    // 模拟 8001做了 6次任务后掉线
    if ("8001".equals(port) && count++ >= 6) {
      return;
    }
    redisTemplate.opsForValue().set(taskName, port, 7, TimeUnit.SECONDS);
    System.out.println("该喝水了!--" + port + "--线程" + Thread.currentThread().getName()
        + "--" + DateFormat.getDateTimeInstance().format(new Date()));
  }
}

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值