spring的定时任务schedule

创建定时任务

import com.babyeye.dao.UserDAO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;

/**
 * @author Holley
 * @Description 定时任务
 * @create 2018-08-30 14:29
 **/
@Service
public class ScheduledTaskService {

    private Logger log = LoggerFactory.getLogger(ScheduledTaskService.class);

    private SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-DD hh:mm:ss");

    @Autowired
    private UserDAO userDAO;

    /**
      * @Author holley
      * @Description 每天早上八点将到期账号设置为已过期状态
      * @Date 2018/8/30 15:04
      * @Param []
      * @return void
      */
    @Scheduled(cron = "0 0 8 * * ?")
    public void accountOverdue(){
        userDAO.overdueAccount();
    }

    /**
      * @Author holley
      * @Description 延迟5秒执行一次
      * @Date 2018/8/30 15:35
      * @Param []
      * @return void
      */
    @Scheduled(fixedDelay = 5000)
    public void testFixedDelay(){
        log.info("执行fixedDelay测试方法:" + sdf.format(System.currentTimeMillis()));
    }
    /**
      * @Author holley
      * @Description 5秒执行一次
      * @Date 2018/8/30 15:36
      * @Param []
      * @return void
      */
    @Scheduled(fixedRate = 5000)
    public void testFixedRate(){
        log.info("执行fixedRate测试方法:" + sdf.format(System.currentTimeMillis()));
    }
    /**
      * @Author holley
      * @Description 第一次延迟三秒执行,之后每5秒执行一次
      * @Date 2018/8/30 15:39
      * @Param []
      * @return void
      */
    @Scheduled(initialDelay = 3000,fixedRate = 5000)
    public void testInitialDelay(){
        log.info("执行initialDelay测试方法:" + sdf.format(System.currentTimeMillis()));
    }

}

开启定时任务

import com.babyeye.interceptor.Interceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;


@ServletComponentScan
@SpringBootApplication
@EnableScheduling
public class OkjingApplication {

    public static void main(String[] args) {
        SpringApplication.run(OkjingApplication.class, args);
    }

}

下面开始介绍下secheduled的参数都有哪些:

1.fixedDelay: 表示上一次任务执行多久后再次执行,参数类型是Long,单位毫秒

2.fixedDelayString : 与fixedDelay含义一样,只是参数类型变成了String

3.fixedRate : 表示按照一定的频率执行任务,参数类型是Long,单位毫秒

4.fixedRateString : 和fixedRate含义一样,只是参数类型变成了String

5.initialDelay : 表示延迟多久开始执行第一次任务,参数类型Long,单位毫秒

6.initialDelayString : 与initialDelay含义一样,只是参数类型变成了String

7.cron : cron表达式,指定任务在特定时间执行

  cron表达式定义:

    Cron表达式是一个字符串,由空格隔开的6个或7个域组成,每个域对应一个含义(秒,分,时,日,月,周 ,年),其中年是可选字段,但是spring的scheduled只支持前6个域的表达式,也就是不能设置年,如果超过则会报错

    每个域中可出现的字符类型及含义:

(1)各域支持的字符类型

    秒:可出现", - * /"四个字符,有效范围为0-59的整数  

    分:可出现", - * /"四个字符,有效范围为0-59的整数  

    时:可出现", - * /"四个字符,有效范围为0-23的整数  

    每月第几天:可出现", - * / ? L W C"八个字符,有效范围为0-31的整数  

    月:可出现", - * /"四个字符,有效范围为1-12的整数或JAN-DEc  

    星期:可出现", - * / ? L C #"四个字符,有效范围为1-7的整数或SUN-SAT两个范围。1表示星期天,2表示星期一, 依次类推

(2)特殊字符含义

    * : 表示匹配该域的任意值,比如在秒*, 就表示每秒都会触发事件。;

    ? : 只能用在每月第几天和星期两个域。表示不指定值,当2个子表达式其中之一被指定了值以后,为了避免冲突,需要将另一个子表达式的值设为“?”;

    - : 表示范围,例如在分域使用5-20,表示从5分到20分钟每分钟触发一次  

    / : 表示起始时间开始触发,然后每隔固定时间触发一次,例如在分域使用5/20,则意味着每小时的第5分,25分,45分,分别触发一次.  

    , : 表示列出枚举值。例如:在分域使用5,20,则意味着在5和20分时触发一次。  

    L : 表示最后,只能出现在星期和每月第几天域,如果在星期域使用1L,意味着在最后的一个星期日触发。  

    W : 表示有效工作日(周一到周五),只能出现在每月第几日域,系统将在离指定日期的最近的有效工作日触发事件。注意一点,W的最近寻找不会跨过月份  

    LW : 这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。  

    # : 用于确定每个月第几个星期几,只能出现在每月第几天域。例如在1#3,表示某月的第三个星期日。

   C:代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天。

实例:

  "0 0 * * * *"                      表示每小时0分0秒执行一次

    " */10 * * * * *"                 表示每10秒执行一次

    "0 0 8-10 * * *"                 表示每天8,9,10点执行

    "0 0/30 8-10 * * *"            表示每天8点整开始到10点,每半小时执行

    "0 0 9-17 * * 2-6"     表示每周一至周五,9点到17点的0分0秒执行

    "0 0 0 25 12 ?"                  表示每年圣诞节(12月25日)0时0分0秒执行

 8.zone : 时区,默认为当前时区,一般没有用到

多线程执行

不知道有没有人注意到上面代码执行结果打印出来的log,在截图中我们可以发现被执行的每个任务都是被同一个线程所调用,这是因为在pspring中如果没有自定义设置线程池时,会默认创建一个单线程的线程池。这样的话,如果定时任务增多,就会容易造成阻塞。

如果改成多线程执行,在springboot中有两种方法。

一。实现SchedulingConfigurer接口,重写configureTasks方法即可,代码如下:

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
 
import java.util.concurrent.Executors;
 

@Configuration
//所有的定时任务都放在一个线程池中,定时任务启动时使用不同都线程。
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        //设定一个长度10的定时任务线程池
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(10));
    }
}

二。使用config配置类的方式添加配置,然后需要在定时任务的类或方法上添加@Async注解,代码如下:

@Configuration // 表明该类是一个配置类
@EnableAsync  // 开启异步支持
public class AsyncConfig {
     /*
    此处成员变量应该使用@Value从配置中读取
     */
    private int corePoolSize = 10;
    private int maxPoolSize = 200;
    private int queueCapacity = 10;
    @Bean
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.initialize();
        return executor;
    }
}
/**
 * @author Holley
 * @Description 定时任务
 * @create 2018-08-30 14:29
 **/
@Async
@Service
public class ScheduledTaskService {

    private Logger log = LoggerFactory.getLogger(ScheduledTaskService.class);

    @Autowired
    private UserDAO userDAO;

    /**
      * @Author holley
      * @Description 每天早上八点将到期账号设置为已过期状态
      * @Date 2018/8/30 15:04
      * @Param []
      * @return void
      */
    @Scheduled(cron = "0 0 8 * * ?")
    public void accountOverdue(){
        userDAO.overdueAccount();
    }
}

除了上述的spring task 的scheduled之外,还有如下几种方式可以实现定时任务:

1.Timer:java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务,使用这种方式可以按照某一频率来执行,也可以设置指定时间,demo如下:

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author Holley
 * @Description 请输入一句话进行描述
 * @create 2018-08-31 10:45
 **/
public class TimerDemo {
        public static void main(String[] args) {
            TimerTask timerTask = new TimerTask() {
                @Override
                public void run() {
                    System.out.println("task  run:"+ new Date());
                }
            };
            Timer timer = new Timer();
            System.out.println(new Date());
            //安排指定的任务在指定的时间开始进行重复的固定延迟执行。这里是每3秒执行一次
            timer.schedule(timerTask,10000,3000);
        }
}

执行结果如下:

指定时间执行:

public class TimerTest {

    public static void main(String[] args) {
        Calendar calendar = Calendar.getInstance();
        /**
            * 指定触发的时间
         */
         calendar.set(Calendar.DAY_OF_MONTH,13);//设置日期为13号
         calendar.set(Calendar.MONTH, 3);//设置日期为4月份(月份是从0开始计算)
         calendar.set(Calendar.HOUR_OF_DAY, 12); //设置15点的时候触发
         calendar.set(Calendar.MINUTE, 0); //设置56分钟的时候触发
         calendar.set(Calendar.SECOND, 1); //设置第一秒的时候触发
         Date time = calendar.getTime();
         Timer timer = new Timer();
         timer.schedule(new RemindTask(), time);
    }
}
View Code
public class RemindTask extends TimerTask {
    @Override
    public void run() {
        System.out.println(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
    }
}

详细介绍可以参考:https://blog.csdn.net/u013164931/article/details/80762356

2.ScheduledExcutorService:也是jdk自带的一个类,是基于线程设计的定时任务,每个调度任务都会分配到线程池中的一个线程去执行,因此任务是并发执行的。demo如下:

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author Holley
 * @Description 请输入一句话进行描述
 * @create 2018-08-31 10:58
 **/
public class ScheduleExecutorService {
    public static void main(String[] args) {
        ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
        // java8的Lambda表达式 参数:1、任务体 2、首次执行的延时时间
        //      3、任务执行间隔 4、间隔时间单位
        service.scheduleAtFixedRate(() -> System.out.println("task ScheduledExecutorService " + new Date()), 0, 3, TimeUnit.SECONDS);
    }
}

在此demo中使用了java8的Lambda表达式,如果对此表达式不了解的可以参考下:https://www.cnblogs.com/andywithu/p/7357069.html

 3.Quartz:这是一个功能比较强大的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行,配置起来比较复杂,还需要添加依赖

  如果SpringBoot版本是2.0.0以后的,则在spring-boot-starter中已经包含了quart的依赖,则可以直接使用spring-boot-starter-quartz依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

  如果SpringBoot版本是1.5.9则要使用以下依赖

<dependency>
  <groupId>org.quartz-scheduler</groupId>
  <artifactId>quartz</artifactId>
  <version>2.3.0</version>
</dependency>
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context-support</artifactId>
</dependency>
  • 创建任务类TestQuartz,该类主要是继承了QuartzJobBean
public class TestQuartz extends QuartzJobBean {
    /**
     * 执行定时任务
     * @param jobExecutionContext
     * @throws JobExecutionException
     */
    @Override
    protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("quartz task "+new Date());
    }
}
  • 创建配置类QuartzConfig
    @Configuration
    public class QuartzConfig {
        @Bean
        public JobDetail teatQuartzDetail(){
            return JobBuilder.newJob(TestQuartz.class).withIdentity("testQuartz").storeDurably().build();
        }
    
        @Bean
        public Trigger testQuartzTrigger(){
            SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                    .withIntervalInSeconds(10)  //设置时间周期单位秒
                    .repeatForever();
            return TriggerBuilder.newTrigger().forJob(teatQuartzDetail())
                    .withIdentity("testQuartz")
                    .withSchedule(scheduleBuilder)
                    .build();
        }
    }

    关于Quartz的详细内容可以查看官方文档:http://www.quartz-scheduler.org/documentation/

参考:https://blog.csdn.net/ninifengs/article/details/77141240

      https://blog.csdn.net/wqh8522/article/details/79224290

原文地址:https://www.cnblogs.com/zhlblogs/p/9561733.html