在Spring Schedule进行定时关单任务时候呢,由于项目涉及到分布式,导致遇到多线程上的问题,
最后在Spring Schedule定时关单快速入门(三)的最后也得到了解决。但是解决的方法相对原生。

至此,我们引入Redisson框架进行优化出v4版本的定时器。

1.首先我们要在项目中的pom.xml文件中引入Redisson框架。

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>2.9.0</version>
</dependency>

2.在项目中的pom.xml文件中引入Redisson框架的依赖文件。

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-avro</artifactId>
    <version>2.9.0</version>
</dependency>

3.将Redisson进行初始化

A:首先创建Redisson类,在类上添加上Spring的注解(@Component)使该类被Spring管理。

    B:创建两个主要类成员(config)(redisson)和对应Redis的类成员(RedisIP)(RedisPort)
         config为配置文件,负责加载上Redis的类成员(RedisIP)(RedisPort)
         redisson为主要成员,负责加载上配置文件(config)

    C:添加初始化方法,为方法添加上@PostConstruct注解使该方法在spring加载此类组件(Component)的时候调用此方法。

    D:为主要成员(redisson)提供外界访问方式(getter方法)

Redisson初始化代码如下:

package com.mmall.common;

  import com.mmall.util.PropertiesUtil;
  import lombok.extern.slf4j.Slf4j;
  import org.redisson.Redisson;
  import org.redisson.config.Config;
  import org.springframework.stereotype.Component;

  import javax.annotation.PostConstruct;


  @Component //组件
  @Slf4j
  public class RedissonManager {

      private Config config = new Config();
  
      private Redisson redisson = null;
  
      public Redisson getRedisson() { //提供外界访问使用
          return redisson;
      }
      private static String redis1Ip  = PropertiesUtil.getProperty("redis1.ip");//RedisIP
      private static Integer redis1Port = Integer.parseInt(PropertiesUtil.getProperty("redis1.port"));//RedisPort
      private static String redis2Ip  = PropertiesUtil.getProperty("redis2.ip");
      private static Integer redis2Port = Integer.parseInt(PropertiesUtil.getProperty("redis2.port"));



      @PostConstruct //在spring加载此类组件(Component)的时候调用此方法。
      private void init(){//初始化方法
          //setAddress 需要跟进源码,源码里头有该方法的使用介绍,里头详细的介绍了参数该如何传递
          try {
              config.useSingleServer().setAddress(new StringBuilder().append(redis1Ip).append(":").append(redis1Port).toString());
  
              redisson = (Redisson) Redisson.create(config);

              log.info("初始化Redisson结束");
          } catch (Exception e) {
              log.error("redisson init error",e);
              e.printStackTrace();
          }

      }
     
    //如果不添加下面这个方法,那项目在关闭Tomcat的时候会有线程未关闭。导致Tomcat无法顺利关闭。(重点)
    @PreDestroy  //被@PreConstruct修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreConstruct修饰的方法会在destroy()方法之后运行,在Servlet被彻底卸载之前。
    private void close(){
         redisson.shutdown();
          log.info("Redisson关闭");
    }


  }

定时关单器V4版本(代码)

@Scheduled(cron = "0 */1 * * * ?")//每1分钟(每个1分钟的整数倍)调用一次
public void closeOrderTaskV4(){
    RLock lock = redissonManager.getRedisson().getLock(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);//获取到锁  参数传递锁的名字
    boolean getLock = false;
    try {                     //在参数1中的等待时间中,我们应该预估定时器代码的执行而定,
        //如果项目中定的等待时间是2秒,但是定时器的代码在1秒的时候执行完后释放了锁
        //此时另一个Tomcat的定时器发现在等待2秒的期间发现锁释放了,随即进入了锁定时器。
        //所以在我们不知道定时器代码执行的大概时间时,我们尽可能将等待时间设置为0
        if(getLock = lock.tryLock(0,5, TimeUnit.SECONDS)){//参数1:等待时间2 参数2:工作时间5  参数3:单位秒

            log.info("Redisson获取到分布式锁:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
            int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
            //iOrderService.closeOrder(hour);
        }else{
            log.info("Redisson没有获取到分布式锁:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
        }
    } catch (InterruptedException e) {
        log.error("Redisson分布式锁获取异常",e);
    }finally {
        if(!getLock){
            return;
        }
        lock.unlock();//释放锁
        log.info("Redisson分布式锁释放锁");
    }
}