一、定时任务实现的几种方式:

Timer

这是java自带的java.util.Timer类,这个类允许你调度一个java.util.TimerTask任务。使用这种方式可以让你的程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。

ScheduledExecutorService

也jdk自带的一个类;是基于线程池设计的定时任务类,每个调度任务都会分配到线程池中的一个线程去执行,也就是说,任务是并发执行,互不影响。

Spring Task

Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz,而且使用起来比Quartz简单许多。

Quartz

这是一个功能比较强大的的调度器,可以让你的程序在指定时间执行,也可以按照某一个频度执行,配置起来稍显复杂。

二、基于SpringBoot的定时任务

使用SpringBoot 自带的定时任务,只需要添加相应的注解就可以实现

2.1 导入SpringBoot启动包

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-parent</artifactId>
  <version>2.1.1.RELEASE</version>
</dependency>

 2.2 启动类启用定时

在启动类上面加上@EnableScheduling即可开启定时 或者 再定时器类上添加也可

@SpringBootApplication
@EnableScheduling // 开启定时
public class SpringBootDemoTimeTaskApplication {
  private static final Logger logger = LoggerFactory.getLogger(SpringBootDemoTimeTaskApplication.class);
  public static void main(String[] args) {
    SpringApplication.run(SpringBootDemoTimeTaskApplication.class);
    logger.info("SpringBootDemoTimeTaskApplication start!");
  }
}

2.3 创建定时任务实现类SchedulerTask

@Component
//@EnableScheduling // 开启定时,启动类和这里只需设置一处
public class SchedulerTask {
  private static final Logger logger = LoggerFactory.getLogger(SchedulerTask.class);
  /**
   * @Scheduled(fixedRate = 6000) :上一次开始执行时间点之后6秒再执行
   * @Scheduled(fixedDelay = 6000) :上一次执行完毕时间点之后6秒再执行
   * @Scheduled(initialDelay=1000, fixedRate=6000) :第一次延迟1秒后执行,之后按fixedRate的规则每6秒执行一次
   * @Scheduled(cron=""):详见cron表达式http://www.pppet.net/
   */
  @Scheduled(fixedRate = 5000)
  public void scheduled1() {
    logger.info("=====>>>>>使用fixedRate执行定时任务");
  }
  @Scheduled(fixedDelay = 10000)
  public void scheduled2() {
    logger.info("=====>>>>>使用fixedDelay执行定时任务");
  }
  @Scheduled(cron="*/6 * * * * ?")
  private void scheduled3(){
    logger.info("使用cron执行定时任务");
  }
}

运行结果:

2019-03-09 17:33:05.681 INFO 7752 --- [      main] c.v.t.SpringBootDemoTimeTaskApplication : SpringBootDemoTimeTaskApplication start!
2019-03-09 17:33:06.002 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : 使用cron执行定时任务
2019-03-09 17:33:10.680 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : =====>>>>>使用fixedRate执行定时任务
2019-03-09 17:33:12.003 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : 使用cron执行定时任务
2019-03-09 17:33:15.676 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : =====>>>>>使用fixedRate执行定时任务
2019-03-09 17:33:15.676 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : =====>>>>>使用fixedDelay执行定时任务
2019-03-09 17:33:18.002 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : 使用cron执行定时任务
2019-03-09 17:33:20.677 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : =====>>>>>使用fixedRate执行定时任务
2019-03-09 17:33:24.002 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : 使用cron执行定时任务
2019-03-09 17:33:25.680 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : =====>>>>>使用fixedRate执行定时任务
2019-03-09 17:33:25.681 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : =====>>>>>使用fixedDelay执行定时任务
2019-03-09 17:33:30.005 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : 使用cron执行定时任务
2019-03-09 17:33:30.680 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : =====>>>>>使用fixedRate执行定时任务
2019-03-09 17:33:35.680 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : =====>>>>>使用fixedRate执行定时任务
2019-03-09 17:33:35.682 INFO 7752 --- [  scheduling-1] cn.van.task.service.SchedulerTask    : =====>>>>>使用fixedDelay执行定时任务

2.4 执行时间的配置

在上面的定时任务中,我们在方法上使用@Scheduled注解来设置任务的执行时间,并且使用三种属性配置方式:

fixedRate:定义一个按一定频率执行的定时任务

fixedDelay:定义一个按一定频率执行的定时任务,与上面不同的是,改属性可以配合initialDelay, 定义该任务延迟执行时间。

cron:通过表达式来配置任务执行时间--在线cron表达式生成器

三、多线程执行定时任务

SpringBoot定时任务默认单线程,可以看到三个定时任务都已经执行,并且使同一个线程中(scheduling-1)串行执行,如果只有一个定时任务,这样做肯定没问题,当定时任务增多,如果一个任务卡死,会导致其他任务也无法执行。

3.1 多线程配置类 TaskPoolConfig .class

package com.**.config;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
 
/**
 * @ProjectName: tshare_blsj_server
 * @Package: com.**.config
 * @ClassName: TaskPoolConfig
 * @author: 
 * @description: 线程池配置
 * @date: 
 * @version: 1.0
 */
@EnableAsync
@Configuration
public class TaskPoolConfig {
    @Bean("tbsjTaskExecutor")
    public Executor mailNumPoolSingleTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //设置为1,任务顺序执行
        executor.setCorePoolSize(10);//核心线程数:线程池创建时候初始化的线程数
        executor.setMaxPoolSize(100);//最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setQueueCapacity(20);//缓冲队列200:缓冲执行任务的队列
        executor.setKeepAliveSeconds(60);//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        executor.setThreadNamePrefix("tbsjTaskExecutor-");//线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());// 线程池对拒绝任务的处理策略
        return executor;
    }
 
    @Bean("otherSingleTaskExecutor")
    public Executor otherSingleTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //设置为1,任务顺序执行
        executor.setCorePoolSize(1);//核心线程数:线程池创建时候初始化的线程数
        executor.setMaxPoolSize(1);//最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setQueueCapacity(20);//缓冲队列200:缓冲执行任务的队列
        executor.setKeepAliveSeconds(60);//允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
        executor.setThreadNamePrefix("otherSingleTaskExecutor-");//线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());// 线程池对拒绝任务的处理策略
        return executor;
    }
}

3.2 定时任务加注解

package com.**.task;
 
import com.**.service.StationInfoService;
import com.**.service.TshareStationData2Service;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
 
/**
 * @ProjectName: tshare_blsj_server
 * @Package: com.**.task
 * @ClassName: Data2NowExceptionTask
 * @author: 
 * @description:
 * @date: 
 * @version: 1.0
 */
 
@Slf4j
@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
@EnableAsync
public class Data2NowExceptionTask {
 
    @Autowired
    private StationInfoService stationInfoService;
 
    @Autowired
    private TshareStationData2Service tshareStationData2Service;
 
 
    @Scheduled(cron = "0/10 * * * * *")
    @Async("tbsjTaskExecutor")    // 通过注解指定使用的线程池
    public void det(){
        log.info("Scheduled task is running... ...");
 
    }
}

控制台打印,定时任务执行,可以看到,每次都是不同线程:

配置spring boot绘画超时时间 springboot timer_spring boot

 

当核心线程(10个)都用完之后,第一个线程所在的定时任务已经执行完成,那么,下一个定时任务将继续使用核心线程池中的第一个线程!

当核心线程(10个)都用完之后,第一个线程所在的定时任务尚未执行完成,将开启新的线程去执行定时任务,直到前面有任务执行完成,下个任务才会使用之前的线程去执行; 

配置spring boot绘画超时时间 springboot timer_spring boot_02