定时任务应用非常广泛,Java提供的现有解决方案有很多。
本次主要讲schedule、quartz、xxl-job、shedlock等相关的代码实践。

一、SpringBoot使用Schedule

核心代码:

@Component
public class ScheduleTask { private Logger logger = LoggerFactory.getLogger(ScheduleTask.class); @Scheduled(cron = "0/1 * * * * ? ")
public void one() { logger.info("one:" + new Date());
} @Scheduled(cron = "0/1 * * * * ? ")
public void two() { logger.info("two:" + new Date());
} @Scheduled(cron = "0/1 * * * * ? ")
public void three() { logger.info("three:" + new Date());
}
}

运行效果如下:

除此之外还可以这样实现,核心代码:

@PropertySource(value = {
"classpath:task.properties",
}, encoding = "utf-8")
@Component("scheduleTask")
public class ScheduleTask implements SchedulingConfigurer { @Value("${TEST_JOB_TASK_CRON}")
private String cron; @Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) { scheduledTaskRegistrar.addTriggerTask(new Runnable() { @Override
public void run() {
System.out.println("执行任务:" + DateUtil.date()); } }, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
});
} public void setCron(String cron) {
this.cron = cron;
}
}

有朋友或许很疑惑,为什么要写这么一大堆,这个与前面的代码又有何区别呢?
区别是多线程并行。其实多线程并行也可以不用这么写,只需写一段核心配置类代码即可。

定时任务多线程配置类:

@Configuration
public class ScheduleConfig implements SchedulingConfigurer { public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
}
}

再次启动,查看效果,如下:

由此看出走不同的线程执行,不同的线程执行的好处是,如果某一个线程挂掉后,并不会阻塞导致其它定时任务无法执行。

另外如果要想并发执行,前面的配置可以不要,直接用SpringBoot提供的现成注解即可,核心代码如下:

@Component
@EnableAsync
public class ScheduleAsyncTask { private Logger logger = LoggerFactory.getLogger(ScheduleAsyncTask.class); @Scheduled(cron = "0/1 * * * * ? ")
@Async
public void one() { logger.info("one Async:" + new Date());
} @Scheduled(cron = "0/1 * * * * ? ")
@Async
public void two() { logger.info("two Async:" + new Date());
} @Scheduled(cron = "0/1 * * * * ? ")
@Async
public void three() { logger.info("three Async:" + new Date());
}
}

除此外,还有基于schedule动态定时任务(所谓动态只不过是指cron表达式放在对应的数据表里),简单示例代码:

@Configuration
public class DynamicScheduleTask implements SchedulingConfigurer { @Autowired
@SuppressWarnings("all")
CronMapper cronMapper; @Mapper
public interface CronMapper {
@Select("select cron from cron limit 1")
public String getCron();
} /**
* 执行定时任务.
*/
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.addTriggerTask(
//1.添加任务内容(Runnable)
() -> System.out.println("执行动态定时任务: " + LocalDateTime.now().toLocalTime()),
//2.设置执行周期(Trigger)
triggerContext -> {
//2.1 从数据库获取执行周期
String cron = cronMapper.getCron();
//2.2 合法性校验.
if (StringUtils.isEmpty(cron)) {
// Omitted Code ..
}
//2.3 返回执行周期(Date)
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
);
} }

核心配置文件(application.yml):

spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/test
username: root
password: 1234

SQL脚本:

DROP DATABASE IF EXISTS `test`;
CREATE DATABASE `test`;
USE `test`;
DROP TABLE IF EXISTS `cron`;
CREATE TABLE `cron` (
`cron_id` varchar(30) NOT NULL PRIMARY KEY,
`cron` varchar(30) NOT NULL
);
INSERT INTO `cron` VALUES ('1', '0/5 * * * * ?');

运行效果如下:

二、SpringBoot使用Quartz

1.Maven依赖

<!--引入quartz定时框架-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

2.配置文件

spring:
quartz:
#相关属性配置
properties:
org:
quartz:
scheduler:
instanceName: clusteredScheduler
instanceId: AUTO
jobStore:
class: org.quartz.impl.jdbcjobstore.JobStoreTX
driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
tablePrefix: QRTZ_
isClustered: true
clusterCheckinInterval: 10000
useProperties: false
threadPool:
class: org.quartz.simpl.SimpleThreadPool
threadCount: 10
threadPriority: 5
threadsInheritContextClassLoaderOfInitializingThread: true
#数据库方式
job-store-type: jdbc
#初始化表结构
jdbc:
initialize-schema: always
datasource:
url: jdbc:mysql://127.0.0.1:3306/test
username: root
password: 1234

3.启动类

@SpringBootApplication
@EnableScheduling
public class BlogQuartzApplication { public static void main(String[] args) {
SpringApplication.run(BlogQuartzApplication.class, args);
} }

4.配置类

@Configuration
public class QuartzConfiguration {
// 使用jobDetail包装job
@Bean
public JobDetail myCronJobDetail() {
return JobBuilder.newJob(CouponTimeOutJob.class).withIdentity("couponTimeOutJob").storeDurably().build();
} // 把jobDetail注册到Cron表达式的trigger上去
@Bean
public Trigger CronJobTrigger() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ?"); return TriggerBuilder.newTrigger()
.forJob(myCronJobDetail())
.withIdentity("CouponTimeOutJobTrigger")
.withSchedule(cronScheduleBuilder)
.build();
}
}

5.定时任务类

public class CouponTimeOutJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("定时任务执行");
}
}

6.启动成功不报错

(1)对应的数据库会生成定时任务相关的数据表

(2)控制台不断输出定时任务执行日志

三、SpringBoot使用xxl-job

之前写过一样的例子,如今简化了下。
关于xxl-job使用详情,可以参考我的这篇文章:
SpringBoot整合Xxl-Job

1.Maven依赖

<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.2.0</version>
</dependency>

2.配置类

@Configuration
public class XxlJobConfig {
private Logger logger = LoggerFactory.getLogger(XxlJobConfig.class); @Value("${xxl.job.admin.addresses}")
private String adminAddresses; @Value("${xxl.job.executor.appname}")
private String appName; @Value("${xxl.job.executor.ip}")
private String ip; @Value("${xxl.job.executor.port}")
private int port; @Value("${xxl.job.accessToken}")
private String accessToken; @Value("${xxl.job.executor.logpath}")
private String logPath; @Value("${xxl.job.executor.logretentiondays}")
private int logRetentionDays; @Bean(initMethod = "start", destroyMethod = "destroy")
public XxlJobSpringExecutor xxlJobExecutor() {
logger.info(">>>>>>>>>>> xxl-job config init.");
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appName);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays); return xxlJobSpringExecutor;
} }

3.配置文件内容

# web port
server.port=8081
# no web
#spring.main.web-environment=false
### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"
xxl.job.admin.addresses=http://127.0.0.1:8080/xxl-job-admin
### xxl-job, access token
xxl.job.accessToken=
### xxl-job executor appname
xxl.job.executor.appname=blog-job-xxl-job
### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is null
xxl.job.executor.address=
### xxl-job executor server-info
xxl.job.executor.ip=
xxl.job.executor.port=8888
### xxl-job executor log-path
xxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler
### xxl-job executor log-retention-days
xxl.job.executor.logretentiondays=30

4.定时任务类

@Component
public class XxlJobTaskExample { @XxlJob("blogJobHandler")
public ReturnT<String> blogJobHandler(String param) throws Exception {
System.out.println("执行");
XxlJobLogger.log("XXL-JOB, Hello World."); for (int i = 0; i < 5; i++) {
XxlJobLogger.log("beat at:" + i);
TimeUnit.SECONDS.sleep(2);
}
return ReturnT.SUCCESS;
} }

5.执行效果

分别如下所示:

四、SpringBoot使用ShedLock

1.导入Maven依赖

<!-- 分布式定时任务锁 -->
<!-- https://mvnrepository.com/artifact/net.javacrumbs.shedlock/shedlock-spring -->
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-spring</artifactId>
<version>4.0.4</version>
</dependency>
<!-- 使用redis做分布式任务 -->
<dependency>
<groupId>net.javacrumbs.shedlock</groupId>
<artifactId>shedlock-provider-redis-spring</artifactId>
<version>2.5.0</version>
</dependency>
<!-- redis组件 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2.编写配置类

@Configuration
@EnableSchedulerLock(defaultLockAtMostFor = "PT30M")
public class ShedLockConfig { @Bean
public LockProvider lockProvider(RedisTemplate redisTemplate) {
return new RedisLockProvider(redisTemplate.getConnectionFactory()); } }

3.编写具体的定时任务

@Component
public class TaskSchedule { /**
* 每分钟执行一次
* [秒] [分] [小时] [日] [月] [周] [年]
*/
@Scheduled(cron = "1 * * * * ?")
@SchedulerLock(name = "synchronousSchedule")
public void SynchronousSchedule() { System.out.println("Start run schedule to synchronous data:" + new Date()); }
}

4.编写启动类

@SpringBootApplication
@EnableScheduling
public class ShedLockRedisApplication {
public static void main(String[] args) {
SpringApplication.run(ShedLockRedisApplication.class);
}
}

5.配置文件

server:
tomcat:
uri-encoding: UTF-8
max-threads: 1000
min-spare-threads: 30
port: 8083 spring:
redis:
database: 0
host: localhost
port: 6379
password: # 密码(默认为空)
timeout: 6000ms # 连接超时时长(毫秒)
jedis:
pool:
max-active: 1000 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 5 # 连接池中的最小空闲连接

6.测试

我之所以用shedlock是因为确保在集群环境下各微服务的定时任务只执行一个,而不是全部都运行相同的定时任务。

本次测试效果如下:

本次代码例子已放至我的GitHub:
https://github.com/developers-youcong/blog-job

Java之定时任务全家桶的更多相关文章

  1. Java秋招面试复习大纲(二):Spring全家桶+MyBatis+MongDB+微服务

    前言 对于那些想面试高级 Java 岗位的同学来说,除了算法属于比较「天方夜谭」的题目外,剩下针对实际工作的题目就属于真正的本事了,热门技术的细节和难点成为了面试时主要考察的内容. 这里说「天方夜谭」 ...

  2. 最全最新java面试题系列全家桶(带答案)

    最全最新java面试题系列全家桶(带答案) 置顶 2019年04月06日 22:40:28 青春季风暴 阅读数 14082 文章标签: java面试题技术栈 更多 分类专栏: 面试   版权声明:本文 ...

  3. 10分钟详解Spring全家桶7大知识点

    Spring框架自2002年诞生以来一直备受开发者青睐,它包括SpringMVC.SpringBoot.Spring Cloud.Spring Cloud Dataflow等解决方案.有人亲切的称之为 ...

  4. 使用教育邮箱激活JetBrains全家桶

    如果你还有在校时的邮箱,比如your_name@xxx.edu或者your_name@xxx.edu.cn的邮箱,那么你可以免费激活JetBrains全家桶. JetBrains Toolbox 专业 ...

  5. ①小姐,来桶全家桶不?(Servlet)

    一.前言 小生不才,大二下半学期第二个星期,近11月博客园.星期六闲来看看经典书.重温下Serlvet. 二.温故而知新:超文本转移协议HTTP HTTP协议是通过互联网(internet)或企业内部 ...

  6. JetBrains全家桶破解思路(最新更新:2019-04-17)

    JetBrains全家桶破解思路(以DataGrip为例) 2019-04-17 add new key 2019-01-24 add new key and code 2018-12-24 add ...

  7. Spring全家桶–SpringBoot Rest API

    Spring Boot通过提供开箱即用的默认依赖或者转换来补充Spring REST支持.在Spring Boot中编写RESTful服务与SpringMVC没有什么不同.总而言之,基于Spring ...

  8. Spring全家桶系列–SpringBoot之AOP详解

    //本文作者:cuifuan //本文将收录到菜单栏:<Spring全家桶>专栏中 面向方面编程(AOP)通过提供另一种思考程序结构的方式来补充面向对象编程(OOP). OOP中模块化的关 ...

  9. Spring全家桶系列–SpringBoot渐入佳境

    //本文作者:cuifuan //本文将收录到菜单栏:<Spring全家桶>专栏中 首发地址:https://www.javazhiyin.com/20913.html 萌新:小哥,我在实 ...

随机推荐

  1. 第十二章 Linux三剑客之老三—grep

    一.Linux grep 命令用于查找文件里符合条件的字符串. Linux系统中的grep命令是一种功能强大的文本搜索工具,它能使用正则表达式搜索文本,并把匹配的行打印出来.grep全称是Global ...

  2. 详解Kubernetes微服务自动化发布系统

    实施微服务架构后,原先单一的系统结构统变成了数量众多的微服务应用,开发.测试.运维部署等都会面临不少挑战.在微服务架构下如何提高工程研发效率,确保开发.测试.运维部署等流程上的顺畅,是微服务技术体系能 ...

  3. Iobuffer的使用

    写模式: 创建Iobuffer实例,使用Iobuffer的static方法-allocate,有一个参数的方法或者两个参数,第一个参数capacity是指定创建的Iobuffer的容量的最大值,需要注 ...

  4. Learning Attention-based Embeddings for Relation Prediction in Knowledge Graphs

    这篇论文试图将GAT应用于KG任务中,但是问题是知识图谱中实体与实体之间关系并不相同,因此结构信息不再是简单的节点与节点之间的相邻关系.这里进行了一些小的trick进行改进,即在将实体特征拼接在一起的 ...

  5. 博客主题——cnbook

    主题预览 主题下载 gshang.cnbook.rar version:2020-05-29 gshang.cnbook@2.0.rar version:2020-08-25 .source-down ...

  6. 手撸ORM浅谈ORM框架之Delete篇

    快速传送 手撸ORM浅谈ORM框架之基础篇 手撸ORM浅谈ORM框架之Add篇 手撸ORM浅谈ORM框架之Update篇 手撸ORM浅谈ORM框架之Delete篇 手撸ORM浅谈ORM框架之Query ...

  7. STM32入门系列-开发工具keil5安装

    主要介绍如下三部分内容: keil5软件获取 keil5安装 安装STM32芯片包 软件获取 可以通过搜索引擎搜索关键字"KEIL5下载",找到其官方网站www.keil.com. ...

  8. 05 . Go+Vue开发一个线上外卖应用(Session集成及修改用户头像到Fastdfs)

    用户头像上传 功能介绍 在用户中心中,允许用户更换自己的头像.因此,我们开发上传一张图片到服务器,并保存成为用户的头像. 接口解析 在用户模块的控制器MemberController中,解析头像上传的 ...

  9. Linux的进程、线程、文件描述符是什么

    说到进程,恐怕面试中最常见的问题就是线程和进程的关系了,那么先说一下答案:在 Linux 系统中,进程和线程几乎没有区别. Linux 中的进程就是一个数据结构,看明白就可以理解文件描述符.重定向.管 ...

  10. canvas生成圆图和微信小程序canvas圆图

    先在HTML中创建 img和canvas并设置id属性 <canvas id="canvas" width="500" height="500& ...