Java實(shí)現(xiàn)定時(shí)任務(wù)
本文實(shí)例為大家分享了Java實(shí)現(xiàn)定時(shí)任務(wù)的具體代碼,供大家參考,具體內(nèi)容如下
1 使用java.util.Timer
這種方式的定時(shí)任務(wù)主要用到兩個(gè)類,Timer 和 TimerTask,使用起來(lái)比較簡(jiǎn)單。其中 Timer 負(fù)責(zé)設(shè)定 TimerTask 的起始與間隔執(zhí)行時(shí)間。 TimerTask是一個(gè)抽象類,new的時(shí)候?qū)崿F(xiàn)自己的 run 方法,然后將其丟給 Timer 去執(zhí)行即可。
代碼示例:
import java.time.LocalDateTime; import java.util.Timer; import java.util.TimerTask; public class Schedule { ? ? public static void main(String[] args) { ? ? ? ? TimerTask timerTask = new TimerTask() { ? ? ? ? ? ? @Override ? ? ? ? ? ? public void run() { ? ? ? ? ? ? ? ? System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + " 當(dāng)前時(shí)間" + LocalDateTime.now()); ? ? ? ? ? ? } ? ? ? ? }; ? ? ? ? // 在指定延遲0毫秒后開(kāi)始,隨后地執(zhí)行以2000毫秒間隔執(zhí)行timerTask? ? ? ? ? new Timer().schedule(timerTask, 0L, 2000L); ? ? ? ? System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + " 當(dāng)前時(shí)間" + LocalDateTime.now()); ? ? } }
缺點(diǎn):
- Timer 的背后只有一個(gè)線程,不管有多少個(gè)任務(wù),都只有一個(gè)工作線程串行執(zhí)行,效率低下
- 受限于單線程,如果第一個(gè)任務(wù)邏輯上死循環(huán)了,后續(xù)的任務(wù)一個(gè)都得不到執(zhí)行
- 依然是由于單線程,任一任務(wù)拋出異常后,整個(gè) Timer 就會(huì)結(jié)束,后續(xù)任務(wù)全部都無(wú)法執(zhí)行
2 使用ScheduledExecutorService
ScheduledExecutorService 即是 Timer 的替代者,JDK 1.5 并發(fā)包引入,是基于線程池設(shè)計(jì)的定時(shí)任務(wù)類。每個(gè)調(diào)度任務(wù)都會(huì)分配到線程池中的某一個(gè)線程去執(zhí)行,任務(wù)就是并發(fā)調(diào)度執(zhí)行的,任務(wù)之間互不影響。
Java 5.0引入了java.util.concurrent包,其中的并發(fā)實(shí)用程序之一是ScheduledThreadPoolExecutor ,它是一個(gè)線程池,用于以給定的速率或延遲重復(fù)執(zhí)行任務(wù)。它實(shí)際上是Timer/TimerTask組合的更通用替代品,因?yàn)樗试S多個(gè)服務(wù)線程,接受各種時(shí)間單位,并且不需要子類TimerTask (只需實(shí)現(xiàn)Runnable)。使用一個(gè)線程配置ScheduledThreadPoolExecutor使其等效于Timer 。
代碼示例:
import java.time.LocalDateTime; import java.util.concurrent.*; public class Schedule { ? ? public static void main(String[] args) { ? ? ? ? // 創(chuàng)建一個(gè)ScheduledThreadPoolExecutor線程池,核心線程數(shù)為5 ? ? ? ? ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(5); ? ? ? ? // 創(chuàng)建Runnable打印當(dāng)前線程和當(dāng)前時(shí)間 ? ? ? ? Runnable r = () -> System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + " 當(dāng)前時(shí)間" + LocalDateTime.now()); ? ? ? ? /** ? ? ? ? ?* schedule:只執(zhí)行一次調(diào)度 ? ? ? ? ?* scheduleAtFixedRate:一開(kāi)始就計(jì)算間隔時(shí)間,如果任務(wù)超過(guò)間隔時(shí)間,那么就直接開(kāi)始下一個(gè)任務(wù) ? ? ? ? ?* scheduleWithFixedDelay:任務(wù)無(wú)論執(zhí)行多久,都要等待上一輪任務(wù)完成之后再間隔指定時(shí)間,然后才開(kāi)始下一個(gè)任務(wù) ? ? ? ? ?*/ ? ? ? ? ?// 在指定1秒延遲后執(zhí)行r,之后每?jī)擅雸?zhí)行一次 ? ? ? ? scheduledExecutorService.scheduleAtFixedRate(r, 1, 2, TimeUnit.SECONDS); ? ? } }
3 使用Spring Task
Spring Task 底層是基于 JDK 的 ScheduledThreadPoolExecutor 線程池來(lái)實(shí)現(xiàn)的。直接通過(guò)Spring 提供的 @Scheduled 注解即可定義定時(shí)任務(wù),非常方便。
以Spring Boot來(lái)作為示例,步驟為
1.在啟動(dòng)類所在包下創(chuàng)建Schedule 類(在沒(méi)有配置@ComponentScan的情況下,Spring Boot只會(huì)默認(rèn)掃描啟動(dòng)類所在包的spring組件)
2.在該類上添加@Component和@EnableScheduling注解
3.在方法上添加@Scheduled注解,該注解主要參數(shù)如下
String cron() default ""; ?// 支持cron表達(dá)式 long fixedDelay() default -1; ?// 在最后一次調(diào)用結(jié)束和下一次調(diào)用開(kāi)始之間的時(shí)間間隔,以毫秒為單位 String fixedDelayString() default ""; ?// 同上,類似ScheduledExecutorService的scheduleWithFixedDelay long fixedRate() default -1; ?// 在調(diào)用之前的時(shí)間間隔,以毫秒為單位 String fixedRateString() default ""; ?// 同上,類似ScheduledExecutorService的scheduleAtFixedRate long initialDelay() default -1; ?// 在第一次執(zhí)行fixedRate()或fixedDelay()任務(wù)之前要延遲的毫秒數(shù) String initialDelayString() default ""; ?// 同上
代碼示例:
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDateTime; @Component @EnableScheduling public class Schedule { ? ? @Scheduled(fixedRate = 2000L) ? ? public void task() { ? ? ? ? System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + " 當(dāng)前時(shí)間" + LocalDateTime.now()); ? ? } }
- 優(yōu)點(diǎn): 簡(jiǎn)單,輕量,支持 Cron 表達(dá)式
- 缺點(diǎn) :默認(rèn)只支持單機(jī),是單線程的,并且提供的功能比較單一
可以通過(guò)@EnableAsync和 @Async開(kāi)啟多線程
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import java.time.LocalDateTime; @Component @EnableAsync ?// 開(kāi)啟異步多線程 @EnableScheduling public class Schedule { ? ? @Async ? ? @Scheduled(fixedRate = 2000L) ? ? public void task() { ? ? ? ? System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + " 當(dāng)前時(shí)間" + LocalDateTime.now()); ? ? } }
使用@EnableAsync注解后,默認(rèn)情況下,Spring將搜索關(guān)聯(lián)的線程池定義:上下文中的唯一org.springframework.core.task.TaskExecutor
的bean,或者名為“taskExecutor”的java.util.concurrent.Executor
的bean。如果兩者都無(wú)法解析,則將使用org.springframework.core.task.SimpleAsyncTaskExecutor來(lái)處理異步方法調(diào)用。
TaskExecutor實(shí)現(xiàn)為每個(gè)任務(wù)啟動(dòng)一個(gè)新線程,異步執(zhí)行它。 支持通過(guò)“concurrencyLimit”bean 屬性限制并發(fā)線程。默認(rèn)情況下,并發(fā)線程數(shù)是無(wú)限的,所以使用默認(rèn)的線程池有導(dǎo)致內(nèi)存溢出的風(fēng)險(xiǎn)。
注意:剛才的運(yùn)行結(jié)果看起來(lái)是線程復(fù)用的,而實(shí)際上此實(shí)現(xiàn)不重用線程!應(yīng)盡量實(shí)現(xiàn)一個(gè)線程池TaskExecutor ,特別是用于執(zhí)行大量短期任務(wù)。不要使用默認(rèn)的SimpleAsyncTaskExecutor。
import org.springframework.context.annotation.Bean; import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.concurrent.Executor; @Component @EnableAsync @EnableScheduling public class Schedule { ? ? @Async ? ? @Scheduled(fixedRate = 2000L) ? ? public void task() { ? ? ? ? System.out.println("當(dāng)前線程:" + Thread.currentThread().getName() + " 當(dāng)前時(shí)間" + LocalDateTime.now()); ? ? } ? ? @Bean("taskExecutor") ? ? public Executor taskExecutor() { ? ? ? ? ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); ? ? ? ? taskExecutor.setCorePoolSize(10); ? ? ? ? taskExecutor.setMaxPoolSize(50); ? ? ? ? taskExecutor.setQueueCapacity(200); ? ? ? ? taskExecutor.setKeepAliveSeconds(60); ? ? ? ? taskExecutor.setThreadNamePrefix("自定義-"); ? ? ? ? taskExecutor.setAwaitTerminationSeconds(60); ? ? ? ? return taskExecutor; ? ? } }
上面提到的一些定時(shí)任務(wù)的解決方案都是在單機(jī)下執(zhí)行的,適用于比較簡(jiǎn)單的定時(shí)任務(wù)場(chǎng)景比如每天凌晨備份一次數(shù)據(jù)。如果我們需要一些高級(jí)特性比如支持任務(wù)在分布式場(chǎng)景下的分片和高可用的話,我們就需要用到分布式任務(wù)調(diào)度框架了,比如Quartz、Elastic-Job、XXL-JOB、PowerJob,本文就不再詳細(xì)進(jìn)行介紹了,感興趣的可以自行查閱相關(guān)資料。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java定時(shí)任務(wù)的三種實(shí)現(xiàn)方法
- Java定時(shí)任務(wù)的三種實(shí)現(xiàn)方式
- Quartz實(shí)現(xiàn)JAVA定時(shí)任務(wù)的動(dòng)態(tài)配置的方法
- Java定時(shí)任務(wù):利用java Timer類實(shí)現(xiàn)定時(shí)執(zhí)行任務(wù)的功能
- Java實(shí)現(xiàn)終止線程池中正在運(yùn)行的定時(shí)任務(wù)
- java定時(shí)任務(wù)Timer和TimerTask使用詳解
- Java 實(shí)現(xiàn)定時(shí)任務(wù)的三種方法
- 最流行的java后臺(tái)框架spring quartz定時(shí)任務(wù)
- java中 spring 定時(shí)任務(wù) 實(shí)現(xiàn)代碼
- 在Java Web項(xiàng)目中添加定時(shí)任務(wù)的方法
相關(guān)文章
基于java web獲取網(wǎng)頁(yè)訪問(wèn)次數(shù)代碼實(shí)例
這篇文章主要介紹了基于java web獲取網(wǎng)頁(yè)訪問(wèn)次數(shù)代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02使用SpringBoot進(jìn)行身份驗(yàn)證和授權(quán)的示例詳解
在廣闊的 Web 開(kāi)發(fā)世界中,身份驗(yàn)證是每個(gè)數(shù)字領(lǐng)域的守護(hù)者,在本教程中,我們將了解如何以本機(jī)方式保護(hù)、驗(yàn)證和授權(quán) Spring-Boot 應(yīng)用程序的用戶,并遵循框架的良好實(shí)踐,希望對(duì)大家有所幫助2023-11-11SpringMVC 上傳文件 MultipartFile 轉(zhuǎn)為 File的方法
這篇文章主要介紹了SpringMVC 上傳文件 MultipartFile 轉(zhuǎn)為 File的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02SpringBoot異常: nested exception is java.lang.NoClassDefFoundE
這篇文章主要介紹了SpringBoot異常: nested exception is java.lang.NoClassDefFoundError: javax/servlet/ServletContext解決方案,說(shuō)明了錯(cuò)誤原因和解決方案,需要的朋友可以參考下2021-06-06詳解Java利用深度優(yōu)先遍歷解決迷宮問(wèn)題
深度優(yōu)先遍歷:深度優(yōu)先遍歷是圖論中的經(jīng)典算法。其利用了深度優(yōu)先搜索算法可以產(chǎn)生目標(biāo)圖的相應(yīng)拓?fù)渑判虮?,采用拓?fù)渑判虮砜梢越鉀Q很多相關(guān)的圖論問(wèn)題,如最大路徑問(wèn)題等等。本文將利用深度優(yōu)先遍歷解決迷宮問(wèn)題,感興趣的可以了解一下2022-02-02利用Java中Calendar計(jì)算兩個(gè)日期之間的天數(shù)和周數(shù)
Java 語(yǔ)言的Calendar(日歷),Date(日期),和DateFormat(日期格式)組成了Java標(biāo)準(zhǔn)的一個(gè)基本但是非常重要的部分。日期是商業(yè)邏輯計(jì)算一個(gè)關(guān)鍵的部分。下面這篇文章就給大家介紹了如何利用Java中Calendar計(jì)算兩個(gè)日期之間的天數(shù)和周數(shù),下面來(lái)一起看看吧。2016-12-12Java基礎(chǔ)之Math和Random類知識(shí)總結(jié)
今天帶大家來(lái)學(xué)習(xí)java的Math和Random類,文中有非常詳細(xì)的代碼示例及介紹,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們很有幫助喲,需要的朋友可以參考下2021-05-05SpringBoot實(shí)現(xiàn)動(dòng)態(tài)定時(shí)任務(wù)的示例代碼
在SpringBoot項(xiàng)目中簡(jiǎn)單使用定時(shí)任務(wù),不過(guò)由于要借助cron表達(dá)式且都提前定義好放在配置文件里,不能在項(xiàng)目運(yùn)行中動(dòng)態(tài)修改任務(wù)執(zhí)行時(shí)間,實(shí)在不太靈活?,F(xiàn)在我們就來(lái)實(shí)現(xiàn)可以動(dòng)態(tài)修改cron表達(dá)式的定時(shí)任務(wù),感興趣的可以了解一下2022-10-10SpringBoot?Security使用MySQL實(shí)現(xiàn)驗(yàn)證與權(quán)限管理
安全管理是軟件系統(tǒng)必不可少的的功能。根據(jù)經(jīng)典的“墨菲定律”——凡是可能,總會(huì)發(fā)生。如果系統(tǒng)存在安全隱患,最終必然會(huì)出現(xiàn)問(wèn)題,這篇文章主要介紹了SpringBoot安全管理Spring?Security基本配置2022-11-11