@Scheduled定时任务搭配Redis防止多实例定时重复调用

有个Redis安装使用教程,可视化界面,有需要的话,可以打开这个链接去看一下 

https://blog.csdn.net/weixin_45507672/article/details/105973279

创建个maven项目,在pom.xml文件加上以下依赖

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
</dependency>

以下是Redis的配置文件 

## redis所在的服务器IP
spring.redis.host=127.0.0.1
## 端口
spring.redis.port=6379
## 密码,我这里没有设置,所以不填
spring.redis.password=
## 设置最大连接数,0为无限
spring.redis.pool.max-active=8
## 选择哪个Redis库,可忽略,默认是0
spring.redis.database=1

接着然后是创建Redis配置类

package com.example.schedulingtasks.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

  @Bean
  @SuppressWarnings("all")
  public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
      RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
      template.setConnectionFactory(factory);
      Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
      ObjectMapper om = new ObjectMapper();
      om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
      om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
      jackson2JsonRedisSerializer.setObjectMapper(om);
      StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();

      // key采用String的序列化方式
      template.setKeySerializer(stringRedisSerializer);
      // hash的key也采用String的序列化方式
      template.setHashKeySerializer(stringRedisSerializer);
      // value序列化方式采用jackson
      template.setValueSerializer(jackson2JsonRedisSerializer);
      // hash的value序列化方式采用jackson
      template.setHashValueSerializer(jackson2JsonRedisSerializer);
      template.afterPropertiesSet();

      return template;
  }


}

接着创建一个定时任务Bean

/*
 * Copyright 2012-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *	  https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.schedulingtasks;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import net.javacrumbs.shedlock.spring.annotation.SchedulerLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class ScheduledTasks {

	private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);

	private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

	private static int setnx=0;

	@Autowired
	private RedisTemplate<String,String> redisTemplate;

	ExecutorService executorService = Executors.newCachedThreadPool();
	@Scheduled(cron = "0/20 * * * * ?")
	public void reportCurrentTimeOne() throws InterruptedException {
//		log.info("进来方法1了 {}", dateFormat.format(new Date()));
		Boolean status1 = redisTemplate.opsForValue().setIfAbsent("status", "1");
		if (status1){
			String status111 = redisTemplate.opsForValue().get("status");
			log.info("The time1 is do {},{}", dateFormat.format(new Date()),status111);
			for (int i = 0; i < 10000; i++) {
				redisTemplate.opsForValue().get("a");
			}

		} else {
			String status = redisTemplate.opsForValue().get("status");
			if (status.equals("3")){
				log.info("(方法1)有其它定时器已经在执行 {}",dateFormat.format(new Date()));

			}else {
				Boolean status11 = redisTemplate.delete("status");
				log.info("方法1:delete{}",status11);
				log.info("(方法1)有其它定时器已经在执行 {}",dateFormat.format(new Date()));
			}
		}
	}

	@Scheduled(cron = "0/20 * * * * ?")
	public void reportCurrentTimeTwo() throws InterruptedException {
//		log.info("进来方法2了 {}", dateFormat.format(new Date()));
		Boolean status2 = redisTemplate.opsForValue().setIfAbsent("status", "2");
		if (status2) {
			String status222 = redisTemplate.opsForValue().get("status");
			log.info("The time2 is do {},{}", dateFormat.format(new Date()),status222);
			for (int i = 0; i < 10000; i++) {
				redisTemplate.opsForValue().get("a");
			}
		} else {
			String status = redisTemplate.opsForValue().get("status");
			if (status.equals("1")){
				log.info("(方法2)有其它定时器已经在执行 {}",dateFormat.format(new Date()));

			}else {
				Boolean status22 = redisTemplate.delete("status");
				log.info("方法2:delete{}",status22);
				log.info("(方法2)有其它定时器已经在执行 {}",dateFormat.format(new Date()));
			}
		}
	}
	@Scheduled(cron = "0/20 * * * * ?")
	public void reportCurrentTimeThree() throws InterruptedException {
//		log.info("进来方法2了 {}", dateFormat.format(new Date()));
		Boolean status3 = redisTemplate.opsForValue().setIfAbsent("status", "3");
		if (status3) {
			String status333 = redisTemplate.opsForValue().get("status");
			log.info("The time3 is do {},{}", dateFormat.format(new Date()),status333);
			for (int i = 0; i < 10000; i++) {
				redisTemplate.opsForValue().get("a");
			}
		} else {
			String status = redisTemplate.opsForValue().get("status");
			if (status.equals("2")){
				log.info("(方法3)有其它定时器已经在执行 {}",dateFormat.format(new Date()));

			}else{
				Boolean status33 = redisTemplate.delete("status");
				log.info("方法3:delete{}",status33);
				log.info("(方法3)有其它定时器已经在执行 {}",dateFormat.format(new Date()));
			}
		}
	}
	//个人觉得在多线程下,fixedRate和fixedDelay是没有啥区别的
//	@Scheduled(fixedDelay = 5000)
//	public void runfixedDelay() {
//		System.out.println("fixedDelay定时任务开始了---------------------"+new Date());
//		for (int i = 0; i <= 10; i++) {
//			executorService.execute(new SubThread(i));
//		}
//	}

//	@Scheduled(fixedRate = 5000)
//	public void runfixedRate() {
//		System.out.println("fixedRate定时任务开始了---------------------"+new Date());
//		for (int i = 0; i <= 10; i++) {
//			executorService.execute(new SubThread(i));
//		}
//	}

//	@Scheduled(fixedRate = 5000)
//	public void runfixedRatesingle() throws InterruptedException {
//		//加入逻辑执行时间超过定时器设定时间,那么会在逻辑执行完毕后的第一时间继续调用
//		//也就是第一次执行时间是12:00:04  逻辑执行需要8秒,就是12:00:12执行完成
//		//那么下一次定时器执行方法的时间就是12:00:12
//		System.out.println("runfixedRatesingle定时任务开始了---------------------" + new Date());
//		Thread.sleep(6*1000);
//		System.out.println("执行完毕" + new Date());
//	}

//	@Scheduled(fixedDelay = 5000)
//	public void runfixedDelaysingle() throws InterruptedException {
//		//加入逻辑执行时间超过定时器设定时间,那么会在逻辑执行完毕后需要等待定时器设定的时间后继续调用
//		//也就是第一次执行时间是12:00:04  逻辑执行需要8秒,就是12:00:12执行完成
//		//那么下一次定时器执行方法的时间就是12:00:20
//		System.out.println("runfixedDelayingle定时任务开始了---------------------" + new Date());
//		Thread.sleep(8*1000);
//		System.out.println("执行完毕" + new Date());
//	}

	class SubThread extends Thread{
		private final int i;
		public SubThread(int i){
			this.i=i;
		}
		@Override
		public void run() {
			try {
				//要是执行方法的时间不够,定时任务就算启动了,也要等上一个任务执行完毕后才会执行
				Thread.sleep(8000);
			} catch (InterruptedException e) {
				throw new RuntimeException(e);
			}
			System.out.println("线程名称"+Thread.currentThread().getName()+"当前的任务"+i+new Date());
		}
	}

//“30 * * * * ?” 每半分钟触发任务
//“30 10 * * * ?” 每小时的10分30秒触发任务
//“30 10 1 * * ?” 每天1点10分30秒触发任务
//“30 10 1 20 * ?” 每月20号1点10分30秒触发任务
//“30 10 1 20 10 ? *” 每年10月20号1点10分30秒触发任务
//“30 10 1 20 10 ? 2011” 2011年10月20号1点10分30秒触发任务
//“30 10 1 ? 10 * 2011” 2011年10月每天1点10分30秒触发任务
//“30 10 1 ? 10 SUN 2011” 2011年10月每周日1点10分30秒触发任务
//“15,30,45 * * * * ?” 每15秒,30秒,45秒时触发任务
//“15-45 * * * * ?” 15到45秒内,每秒都触发任务
//“15/5 * * * * ?” 每分钟的每15秒开始触发,每隔5秒触发一次
//“15-30/5 * * * * ?” 每分钟的15秒到30秒之间开始触发,每隔5秒触发一次
//“0 0/3 * * * ?” 每小时的第0分0秒开始,每三分钟触发一次
//“0 15 10 ? * MON-FRI” 星期一到星期五的10点15分0秒触发任务
//“0 15 10 L * ?” 每个月最后一天的10点15分0秒触发任务
//“0 15 10 LW * ?” 每个月最后一个工作日的10点15分0秒触发任务
//“0 15 10 ? * 5L” 每个月最后一个星期四的10点15分0秒触发任务
//“0 15 10 ? * 5#3” 每个月第三周的星期四的10点15分0秒触发任务

//	Timer
//	ExecutorService
//	Spring @Scheduled
//  quartz
//	xxljob
//  elastic-job
}

 最后在启动类上加上@EnableScheduling注解

 然后就可以启动运行,从控制台可以看到三个相同的定时任务在时间段内是交换执行的。

下面这个是多实例代码

	@Value("${lock.time}")
	private Long lockTime;

    @Scheduled(cron = "0/20 * * * * ?")
	public void reportCurrentTimeOne() throws InterruptedException {
		Boolean status = redisTemplate.opsForValue().setIfAbsent("status", String.valueOf(System.currentTimeMillis()+lockTime));
		if (status){
			redisTemplate.expire("status",2, TimeUnit.SECONDS);
			log.info("The time is do {},{},逻辑开始执行", dateFormat.format(new Date()),status);
			//想要执行的逻辑代码
			redisTemplate.delete("status");
			log.info("调用:delete方法{},释放分布式锁");
			log.info("逻辑执行完毕");
		} else {
			String status1 = redisTemplate.opsForValue().get("status");
			if (status1 !=null && System.currentTimeMillis() > Long.parseLong(status1)){
				String status2 = redisTemplate.opsForValue().getAndSet("status", String.valueOf(System.currentTimeMillis() + lockTime));
				if (status2==null || (status2!=null&&status1.equals(status2))){
					Boolean status11 = redisTemplate.delete("status");
					log.info("调用:else的delete方法{},释放分布式锁",status11);
				}else {
					log.info("没有获取分布式锁 {}",dateFormat.format(new Date()));
				}
			}else {
				log.info("没有获取分布式锁 {}",dateFormat.format(new Date()));
			}
		}
		log.info("关闭定时任务");
	}
@Value("${lock.time}")
private Long lockTime;这个是在yml里面配置的过期时间,也可以当成你代码所需要执行的时间

复制上面代码,然后同时启动两个实例就好了,两个实例的意思可以理解为你同时把代码拷贝两份,一起执行,Redis配置相同,当然在项目中可能是两个不同的服务。

最后如果不理解的话可以看一下这位老师讲的

https://www.bilibili.com/video/BV1Yi4y1f77X/?spm_id_from=333.999.0.0&vd_source=bd83f14cbe8535431d40933abd64bddf

我感觉讲的挺好,主要是对自己一个技术使用的总结。 

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值