淺析java中常用的定時任務(wù)框架-單體
一、閱讀收獲
1. 了解常用的單體應(yīng)用定時任務(wù)框架
2. 掌握定時任務(wù)在單體中如何使用
二、本章源碼下載
三、Timer+TimerTask
- 這是jdk自帶的java.util.Timer類,這個類允許你調(diào)度一個java.util.TimerTask任務(wù)。
- 使用這種方式可以讓你的程序按照某一個頻度執(zhí)行,但不能在指定時間運行,一般用的較少。
/** * @Description: 1. Timer+TimerTask:(jdk自帶) * 這是java自帶的java.util.Timer類,這個類允許你調(diào)度一個java.util.TimerTask任務(wù)。 * 使用這種方式可以讓你的程序按照某一個頻度執(zhí)行,但不能在指定時間運行。一般用的較少。 * @Author: jianweil * @date: 2021/12/14 13:36 */ public class TimerTest { public static void main(String[] args) { TimerTask timerTask = new TimerTask() { @Override public void run() { System.out.println("task run:" + new Date()); } }; TimerTask timerTask2 = new TimerTask() { @Override public void run() { System.out.println("task2 run:" + new Date()); //多線程并行處理定時任務(wù)時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務(wù)便會自動終止運行,使用ScheduledExecutorService則沒有這個問題。 int i = 1/0; } }; //idea會提示:使用ScheduledExecutorService代替Timer吧 Timer timer = new Timer(); System.out.println("begin:" + new Date()); //安排指定的任務(wù)在指定的時間開始進(jìn)行重復(fù)的固定延遲執(zhí)行。這里是延遲5秒開始執(zhí)行,之后每3秒執(zhí)行一次 timer.schedule(timerTask, 5000, 3000); timer.schedule(timerTask2, 5000, 3000); } }
多線程并行處理定時任務(wù)時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務(wù)便會自動終止運行,使用ScheduledExecutorService則沒有這個問題。
四、ScheduledExecutorService
ScheduledExecutorService也是jdk自帶的定時類,可以替代Timer
package com.ljw.springboottimer.scheduledExecutorservice; import org.apache.commons.lang3.concurrent.BasicThreadFactory; import java.util.Date; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @Description: 2. ScheduledExecutorService代替Timer(jdk自帶) * 多線程并行處理定時任務(wù)時,Timer運行多個TimeTask時,只要其中之一沒有捕獲拋出的異常,其它任務(wù)便會自動終止運行, * 使用ScheduledExecutorService則沒有這個問題。 * @Author: jianweil * @date: 2021/12/14 13:42 */ public class ScheduledExecutorServiceTest { public static void main(String[] args) throws InterruptedException { //當(dāng)所有的非守護(hù)線程結(jié)束時,程序也就終止了,同時會殺死進(jìn)程中的所有守護(hù)線程。反過來說,只要任何非守護(hù)線程還在運行,程序就不會終止。 ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(false).build()); System.out.println("begin:" + new Date()); // 參數(shù):1、任務(wù)體 2、首次執(zhí)行的延時時間 3、任務(wù)執(zhí)行間隔 4、間隔時間單位 //延遲5秒執(zhí)行,之后每3秒執(zhí)行一次 executorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { //do something System.out.println("begin:" + new Date()); } }, 5, 3, TimeUnit.SECONDS); } }
五、Spring Task
spring提供的類,可引入依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
開啟定時任務(wù):@EnableScheduling
使用:在相應(yīng)的任務(wù)方法前加上注解@Scheduled即可
5.1 單線程串行執(zhí)行-@Scheduled
@Scheduled注解默認(rèn)使同一個線程中串行執(zhí)行,如果只有一個定時任務(wù),這樣做肯定沒問題,當(dāng)定時任務(wù)增多,如果一個任務(wù)卡死,會導(dǎo)致其他任務(wù)也無法執(zhí)行。
業(yè)務(wù)測試:
@Component @EnableScheduling public class SpringTaskTest { @Scheduled(cron = "0/5 * * * * *") public void run() throws InterruptedException { System.out.println(Thread.currentThread().getName() + "=====>>>>>使用cron {}" + (System.currentTimeMillis() / 1000)); } }
5.2 多線程并發(fā)運行-@Scheduled+配置定時器的程池(推薦)
- 解決單線程串行執(zhí)行任務(wù)的問題,需要配置定時器的程池,推薦這種方法
- 配置并注入一個TaskScheduler類bean即可
- 配置定時器的線程池類如下:
package com.ljw.springboottimer.springtask; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; /** * @Description: 解決單線程串行執(zhí)行 方式2:@Scheduled+配置定時器的線程池 * @Author: jianweil * @date: 2021/12/14 14:44 */ @Configuration public class TaskSchedulerConfig { /** * 初始化了一個線程池大小為 5 的 TaskScheduler, 避免了所有任務(wù)都用一個線程來執(zhí)行 * * @return */ @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); taskScheduler.setPoolSize(5); taskScheduler.setThreadNamePrefix("TaskSchedulerConfig-ljw"); return taskScheduler; } }
業(yè)務(wù)測試
@Component @EnableScheduling public class SpringTaskTest { @Scheduled(cron = "0/5 * * * * *") public void run() throws InterruptedException { System.out.println(Thread.currentThread().getName() + "=====>>>>>使用cron {}" + (System.currentTimeMillis() / 1000)); } @Scheduled(fixedRate = 5000) public void run1() throws InterruptedException { System.out.println(Thread.currentThread().getName() + "=====>>>>>使用fixedRate {}" + (System.currentTimeMillis() / 1000)); } }
5.3 多線程并發(fā)執(zhí)行-@Scheduled+@Async+配置異步線程池
解決單線程串行執(zhí)行任務(wù)的問題,也可以結(jié)合異步注解@Async實現(xiàn),但這種方法并不推薦,需要兩個注解,代碼編寫的工作量大
還可以解決fixedRate在遇到某些執(zhí)行任務(wù)時間超過配置的時間隔,下次任務(wù)時間到了還要等待上次任務(wù)執(zhí)行完成的情況,這是3.2不能解決的。
配置異步線程池類如下:
package com.ljw.springboottimer.springtask; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; import java.util.concurrent.ThreadPoolExecutor; /** * @Description: 解決單線程串行執(zhí)行 方式1:@Scheduled+@Async+配置異步線程池 * @Author: jianweil * @date: 2021/12/14 14:35 */ @Configuration @EnableAsync public class AsyncConfig implements AsyncConfigurer { /** * 定義@Async默認(rèn)的線程池 * ThreadPoolTaskExecutor不是完全被IOC容器管理的bean,可以在方法上加上@Bean注解交給容器管理,這樣可以將taskExecutor.initialize()方法調(diào)用去掉,容器會自動調(diào)用 * * @return */ @Override public Executor getAsyncExecutor() { int processors = Runtime.getRuntime().availableProcessors(); //常用的執(zhí)行器 ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); //核心線程數(shù) taskExecutor.setCorePoolSize(10); taskExecutor.setMaxPoolSize(50); //線程隊列最大線程數(shù),默認(rèn):50 taskExecutor.setQueueCapacity(100); //線程名稱前綴 taskExecutor.setThreadNamePrefix("AsyncConfig-ljw-"); taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //執(zhí)行初始化(重要) taskExecutor.initialize(); return taskExecutor; } }
業(yè)務(wù)測試需要加上@Async注解
@Component @EnableScheduling public class SpringTaskTest { @Scheduled(cron = "0/5 * * * * *") @Async public void run() throws InterruptedException { System.out.println(Thread.currentThread().getName() + "=====>>>>>使用cron {}" + (System.currentTimeMillis() / 1000)); } @Scheduled(fixedRate = 5000) @Async public void run1() throws InterruptedException { System.out.println(Thread.currentThread().getName() + "=====>>>>>使用fixedRate {}" + (System.currentTimeMillis() / 1000)); } }
如果同時配置了3.2配置定時器的程池和3.3配置異步線程池,并且注解使用了@Scheduled+@Async,則定時任務(wù)使用的線程池為:配置異步線程池
5.4 @Scheduled參數(shù)解析
cron:通過cron表達(dá)式來配置任務(wù)執(zhí)行時間(默認(rèn)是fixedDelay)
initialDelay :定義該任務(wù)延遲多少時間才開始第一次執(zhí)行
fixedRate:定義一個按一定頻率執(zhí)行的定時任務(wù)。fixedRate 每次任務(wù)結(jié)束后會從任務(wù)編排表中找下一次該執(zhí)行的任務(wù),判斷是否到時機(jī)執(zhí)行,fixedRate的任務(wù)某次執(zhí)行時間再長也不會造成兩次任務(wù)實例同時執(zhí)行,也要等到上次任務(wù)完成,判斷是否到時機(jī)執(zhí)行,到就立即執(zhí)行,與線程池?zé)o關(guān),除非用了@Async注解,使方法異步,即是使用5.3步驟的配置。(5.2是配置線程池,達(dá)不到效果)
fixedDelay:定義一個按一定頻率執(zhí)行的定時任務(wù)。fixedDelay總是在前一次任務(wù)完成后,延時固定時間長度然后再執(zhí)行下一次任務(wù)
六、Quartz
在開發(fā)Quartz相關(guān)應(yīng)用時,只要定義了Job(任務(wù)),JobDetail(任務(wù)描述),Trigger(觸發(fā)器)和Scheduler(調(diào)度器),即可實現(xiàn)一個定時調(diào)度能力。
如果SpringBoot版本是2.0.0以后的,則在spring-boot-starter中已經(jīng)包含了quart的依賴,則可以直接引入依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-quartz</artifactId> </dependency>
6.1. 創(chuàng)建任務(wù)類
方式1:實現(xiàn)Job類的execute方法即可實現(xiàn)一個任務(wù)(推薦)
任務(wù)1如下:
package com.ljw.springboottimer.quartz.do1; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import java.util.Date; /** * @Description: 我的定時任務(wù)-方法1 * @Author: jianweil * @date: 2021/12/14 16:06 */ public class MyTaskService1 implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { System.out.println(Thread.currentThread().getName() + "------ Job ------" + new Date()); } }
方式2:繼承QuartzJobBean類重寫方法即可實現(xiàn)一個任務(wù)
任務(wù)2如下:
package com.ljw.springboottimer.quartz.do1; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean; import java.util.Date; /** * @Description: 我的定時任務(wù)-方法2 * @Author: jianweil * @date: 2021/12/14 16:06 */ public class MyTaskService2 extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext context) throws JobExecutionException { System.out.println(Thread.currentThread().getName() + "---QuartzJobBean-----" + new Date()); } }
6.2. 配置任務(wù)描述和觸發(fā)器
配置類要分別要為每個任務(wù)聲明兩個bean
- 1.JobDetail(任務(wù)描述)
- 2.Trigger(觸發(fā)器)
配置調(diào)度器信息使用SimpleScheduleBuilder或者CronScheduleBuilder
package com.ljw.springboottimer.quartz.do1;
import org.quartz.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Date;
/**
* @Description: 每個任務(wù)都要兩步配置
* 1.配置任務(wù)描述JobDetail 2. 配置觸發(fā)器Trigger
* @Author: jianweil
* @date: 2021/12/14 16:08
*/
@Configuration
public class QuartzConfig {
/**
* 創(chuàng)建任務(wù)1的 JobDetail1
*
* @return
*/
@Bean
public JobDetail teatQuartzDetail1() {
return JobBuilder.newJob(MyTaskService1.class)
//job的描述
.withDescription("this is a job1")
//job 的name和group
.withIdentity("myTrigger1", "myTriggerGroup1")
.storeDurably().build();
}
/**
* 創(chuàng)建任務(wù)2的 JobDetail2
*
* @return
*/
@Bean
public JobDetail teatQuartzDetail2() {
return JobBuilder.newJob(MyTaskService2.class)
//job的描述
.withDescription("this is a job2")
//job 的name和group
.withIdentity("myTrigger2", "myTriggerGroup2")
.storeDurably().build();
}
/**
* 創(chuàng)建任務(wù)1的 Trigger1
*
* @return
*/
@Bean
public Trigger testQuartzTrigger1() {
//使用SimpleScheduleBuilder或者CronScheduleBuilder
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
//設(shè)置時間周期單位秒
.withIntervalInSeconds(10)
.repeatForever();
//兩秒執(zhí)行一次,Quartz表達(dá)式,支持各種牛逼表達(dá)式
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/3 * * * * ?");
//任務(wù)運行的時間,SimpleSchedle類型觸發(fā)器有效,3秒后啟動任務(wù)
long time = System.currentTimeMillis() + 3 * 1000L;
Date statTime = new Date(time);
return TriggerBuilder.newTrigger()
.withDescription("")
.forJob(teatQuartzDetail1())
.withIdentity("myTrigger1", "myTriggerGroup1")
//默認(rèn)當(dāng)前時間啟動
.startAt(statTime)
.withSchedule(cronScheduleBuilder)
//.withSchedule(scheduleBuilder)
.build();
}
/**
* 創(chuàng)建任務(wù)2的 Trigger2
*
* @return
*/
@Bean
public Trigger testQuartzTrigger2() {
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
//設(shè)置時間周期單位秒
.withIntervalInSeconds(10)
.repeatForever();
return TriggerBuilder.newTrigger()
.forJob(teatQuartzDetail2())
.withIdentity("myTrigger2", "myTriggerGroup2")
.withSchedule(scheduleBuilder)
.build();
}
}
以上就是淺析java中常用的定時任務(wù)框架-單體的詳細(xì)內(nèi)容,更多關(guān)于java定時任務(wù)框架的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
spring boot中interceptor攔截器未生效的解決
這篇文章主要介紹了spring boot中interceptor攔截器未生效的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09Java?集合框架掌握?Map?和?Set?的使用(內(nèi)含哈希表源碼解讀及面試??碱})
這篇文章主要介紹了Java?集合框架掌握?Map?和?Set?的使用并含有內(nèi)含哈希表源碼解讀及面試??碱},?Map?和?Set?是一種適合動態(tài)查找的集合容器或者數(shù)據(jù)結(jié)構(gòu)下面文章詳細(xì)介紹,具有一定的參考價值,需要的小伙伴可以參考一下2021-12-12Java異常中toString()和getMessage()區(qū)別
在java異常體系中,要打印異常信息,可以通過:e.getMessage() 、 e.toString() e.printStackTrace() 等方法打印,本文主要介紹了Java異常中toString()和getMessage()區(qū)別,具有一定的參考價值,感興趣的可以了解一下2024-01-01