Java學(xué)習(xí)教程之定時(shí)任務(wù)全家桶
定時(shí)任務(wù)應(yīng)用非常廣泛,Java提供的現(xiàn)有解決方案有很多。
本次主要講schedule、quartz、xxl-job、shedlock等相關(guān)的代碼實(shí)踐。
一、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());
}
}
運(yùn)行效果如下:

除此之外還可以這樣實(shí)現(xiàn),核心代碼:
@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("執(zhí)行任務(wù):" + DateUtil.date());
}
}, new Trigger() {
@Override
public Date nextExecutionTime(TriggerContext triggerContext) {
return new CronTrigger(cron).nextExecutionTime(triggerContext);
}
});
}
public void setCron(String cron) {
this.cron = cron;
}
}
有朋友或許很疑惑,為什么要寫這么一大堆,這個(gè)與前面的代碼又有何區(qū)別呢?
區(qū)別是多線程并行。其實(shí)多線程并行也可以不用這么寫,只需寫一段核心配置類代碼即可。
定時(shí)任務(wù)多線程配置類:
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
scheduledTaskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
}
}
再次啟動(dòng),查看效果,如下:

由此看出走不同的線程執(zhí)行,不同的線程執(zhí)行的好處是,如果某一個(gè)線程掛掉后,并不會(huì)阻塞導(dǎo)致其它定時(shí)任務(wù)無法執(zhí)行。
另外如果要想并發(fā)執(zhí)行,前面的配置可以不要,直接用SpringBoot提供的現(xiàn)成注解即可,核心代碼如下:
@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動(dòng)態(tài)定時(shí)任務(wù)(所謂動(dòng)態(tài)只不過是指cron表達(dá)式放在對(duì)應(yīng)的數(shù)據(jù)表里),簡(jiǎn)單示例代碼:
@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();
}
/**
* 執(zhí)行定時(shí)任務(wù).
*/
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.addTriggerTask(
//1.添加任務(wù)內(nèi)容(Runnable)
() -> System.out.println("執(zhí)行動(dòng)態(tài)定時(shí)任務(wù): " + LocalDateTime.now().toLocalTime()),
//2.設(shè)置執(zhí)行周期(Trigger)
triggerContext -> {
//2.1 從數(shù)據(jù)庫(kù)獲取執(zhí)行周期
String cron = cronMapper.getCron();
//2.2 合法性校驗(yàn).
if (StringUtils.isEmpty(cron)) {
// Omitted Code ..
}
//2.3 返回執(zhí)行周期(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 * * * * ?');
運(yùn)行效果如下:

二、SpringBoot使用Quartz
1.Maven依賴
<!--引入quartz定時(shí)框架--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
2.配置文件
spring: quartz: #相關(guān)屬性配置 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 #數(shù)據(jù)庫(kù)方式 job-store-type: jdbc #初始化表結(jié)構(gòu) jdbc: initialize-schema: always datasource: url: jdbc:mysql://127.0.0.1:3306/test username: root password: 1234
3.啟動(dòng)類
@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注冊(cè)到Cron表達(dá)式的trigger上去
@Bean
public Trigger CronJobTrigger() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/1 * * * * ?");
return TriggerBuilder.newTrigger()
.forJob(myCronJobDetail())
.withIdentity("CouponTimeOutJobTrigger")
.withSchedule(cronScheduleBuilder)
.build();
}
}
5.定時(shí)任務(wù)類
public class CouponTimeOutJob extends QuartzJobBean {
@Override
protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println("定時(shí)任務(wù)執(zhí)行");
}
}
6.啟動(dòng)成功不報(bào)錯(cuò)
(1)對(duì)應(yīng)的數(shù)據(jù)庫(kù)會(huì)生成定時(shí)任務(wù)相關(guān)的數(shù)據(jù)表

(2)控制臺(tái)不斷輸出定時(shí)任務(wù)執(zhí)行日志

三、SpringBoot使用xxl-job
之前寫過一樣的例子,如今簡(jiǎn)化了下。
關(guān)于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.配置文件內(nèi)容
# 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.定時(shí)任務(wù)類
@Component
public class XxlJobTaskExample {
@XxlJob("blogJobHandler")
public ReturnT<String> blogJobHandler(String param) throws Exception {
System.out.println("執(zhí)行");
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.執(zhí)行效果
分別如下所示:


四、SpringBoot使用ShedLock
1.導(dǎo)入Maven依賴
<!-- 分布式定時(shí)任務(wù)鎖 --> <!-- 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做分布式任務(wù) --> <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.編寫具體的定時(shí)任務(wù)
@Component
public class TaskSchedule {
/**
* 每分鐘執(zhí)行一次
* [秒] [分] [小時(shí)] [日] [月] [周] [年]
*/
@Scheduled(cron = "1 * * * * ?")
@SchedulerLock(name = "synchronousSchedule")
public void SynchronousSchedule() {
System.out.println("Start run schedule to synchronous data:" + new Date());
}
}
4.編寫啟動(dòng)類
@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: # 密碼(默認(rèn)為空) timeout: 6000ms # 連接超時(shí)時(shí)長(zhǎng)(毫秒) jedis: pool: max-active: 1000 # 連接池最大連接數(shù)(使用負(fù)值表示沒有限制) max-wait: -1ms # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒有限制) max-idle: 10 # 連接池中的最大空閑連接 min-idle: 5 # 連接池中的最小空閑連接
6.測(cè)試
我之所以用shedlock是因?yàn)榇_保在集群環(huán)境下各微服務(wù)的定時(shí)任務(wù)只執(zhí)行一個(gè),而不是全部都運(yùn)行相同的定時(shí)任務(wù)。
本次測(cè)試效果如下:

本次代碼例子已放至我的GitHub:https://github.com/developers-youcong/blog-job
到此這篇關(guān)于Java學(xué)習(xí)教程之定時(shí)任務(wù)全家桶的文章就介紹到這了,更多相關(guān)Java定時(shí)任務(wù)全家桶內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java spring定時(shí)任務(wù)詳解
- Java ScheduledExecutorService定時(shí)任務(wù)案例講解
- Java之SpringBoot定時(shí)任務(wù)案例講解
- Java 實(shí)現(xiàn)定時(shí)任務(wù)的三種方法
- java使用@Scheduled注解執(zhí)行定時(shí)任務(wù)
- Java中實(shí)現(xiàn)分布式定時(shí)任務(wù)的方法
- java定時(shí)任務(wù)實(shí)現(xiàn)的4種方式小結(jié)
- JAVA使用quartz添加定時(shí)任務(wù),并依賴注入對(duì)象操作
- Java如何實(shí)現(xiàn)定時(shí)任務(wù)
- Java中定時(shí)任務(wù)的6種實(shí)現(xiàn)方式
相關(guān)文章
Java網(wǎng)絡(luò)編程TCP實(shí)現(xiàn)聊天功能
這篇文章主要為大家詳細(xì)介紹了Java網(wǎng)絡(luò)編程TCP實(shí)現(xiàn)聊天功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07
Java Listener監(jiān)聽器使用規(guī)范詳細(xì)介紹
監(jiān)聽器是一個(gè)專門用于對(duì)其他對(duì)象身上發(fā)生的事件或狀態(tài)改變進(jìn)行監(jiān)聽和相應(yīng)處理的對(duì)象,當(dāng)被監(jiān)視的對(duì)象發(fā)生情況時(shí),立即采取相應(yīng)的行動(dòng)。監(jiān)聽器其實(shí)就是一個(gè)實(shí)現(xiàn)特定接口的普通java程序,這個(gè)程序?qū)iT用于監(jiān)聽另一個(gè)java對(duì)象的方法調(diào)用或?qū)傩愿淖?/div> 2023-01-01
spring boot啟動(dòng)加載數(shù)據(jù)原理分析
實(shí)際應(yīng)用中,我們會(huì)有在項(xiàng)目服務(wù)啟動(dòng)的時(shí)候就去加載一些數(shù)據(jù)或做一些事情這樣的需求。這時(shí)spring Boot 為我們提供了一個(gè)方法,通過實(shí)現(xiàn)接口 CommandLineRunner 來實(shí)現(xiàn)。下面給大家詳細(xì)介紹下,需要的的朋友參考下吧2017-04-04最新評(píng)論

