一、定时任务实现的几种方式:
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... ...");
}
}
控制台打印,定时任务执行,可以看到,每次都是不同线程:
当核心线程(10个)都用完之后,第一个线程所在的定时任务已经执行完成,那么,下一个定时任务将继续使用核心线程池中的第一个线程!
当核心线程(10个)都用完之后,第一个线程所在的定时任务尚未执行完成,将开启新的线程去执行定时任务,直到前面有任务执行完成,下个任务才会使用之前的线程去执行;