java實(shí)現(xiàn)周期性執(zhí)行(定時(shí)任務(wù))
最近是遇到一個(gè)設(shè)備在線離線的判定問(wèn)題,設(shè)計(jì)是每個(gè)多長(zhǎng)時(shí)間(常規(guī)的定時(shí)任務(wù))檢測(cè)一次設(shè)備是否在前,當(dāng)檢測(cè)到里離線時(shí),我們不能立馬判斷為離線,而是要在重試多測(cè)幾次,只要一次成功就返回判定為在線,多次都不成功側(cè)是離線,我這里相當(dāng)了用ScheduledThreadPoolExecutor來(lái)實(shí)現(xiàn),如有不足還請(qǐng)?zhí)岢?。如下?/p>
ScheduledThreadPoolExecutor的介紹:
ScheduledThreadPoolExecutor,它可另行安排在給定的延遲后運(yùn)行命令,或者定期執(zhí)行命令。需要多個(gè)輔助線程時(shí),或者要求 ThreadPoolExecutor 具有額外的靈活性或功能時(shí),此類(lèi)要優(yōu)于Timer。
ScheduledThreadPoolExecutor的使用詳解
當(dāng)程序需要用到一個(gè)定時(shí)器處理問(wèn)題的時(shí)候,并且需要處理的頻率是很快的,這就需要一個(gè)穩(wěn)定的定時(shí)器來(lái)保證數(shù)據(jù)的長(zhǎng)久進(jìn)行。ScheduledThreadPoolExecutor這個(gè)類(lèi)就是個(gè)很好的選擇。正常情況下,定時(shí)器我們都是用Timer和TimerTask這兩個(gè)類(lèi)就能完成定時(shí)任務(wù),并且設(shè)置延長(zhǎng)時(shí)間和循環(huán)時(shí)間間隔。
ScheduledThreadPoolExecutor也能完成Timer一樣的定時(shí)任務(wù),并且時(shí)間間隔更加準(zhǔn)確。
誤差說(shuō)明:
我在后臺(tái)程序看看一下Timer執(zhí)行程序是有可能延遲1、2毫秒,如果是1秒執(zhí)行一次的任務(wù),1分鐘有可能延遲60毫秒,一小時(shí)延遲3600毫秒,相當(dāng)于3秒,實(shí)際用戶(hù)看不出什么區(qū)別。 但是,如果我的程序需要每40毫秒就執(zhí)行一次任務(wù),如果還是有1、2毫秒的誤差,1秒鐘就有25毫秒的誤差,大概40秒就有1秒的誤差,十幾分鐘就有十幾秒的誤差,這對(duì)UI顯示來(lái)說(shuō)是延遲非常嚴(yán)重的了。 而我用ScheduledThreadPoolExecutor來(lái)做40毫秒的間隔任務(wù),一般十幾分鐘才有1秒多的誤差,這個(gè)還是能接受的。 這也是我用ScheduledThreadPoolExecutor這個(gè)類(lèi)的原因。
使用Timer和TimerTask存在一些缺陷:
1.Timer只創(chuàng)建了一個(gè)線程。當(dāng)你的任務(wù)執(zhí)行的時(shí)間超過(guò)設(shè)置的延時(shí)時(shí)間將會(huì)產(chǎn)生一些問(wèn)題。
2.Timer創(chuàng)建的線程沒(méi)有處理異常,因此一旦拋出非受檢異常,該線程會(huì)立即終止。
JDK 5.0以后推薦使用ScheduledThreadPoolExecutor。該類(lèi)屬于Executor Framework,它除了能處理異常外,還可以創(chuàng)建多個(gè)線程解決上面的問(wèn)題
Timer和TimerTask的使用 :
這里就不做過(guò)多的描述了,重點(diǎn)在ScheduledThreadPoolExecutor。
Timer timer = new Timer();
? ? ? timer.schedule(new TimerTask() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? log.e("time:");
?
? ? ? ? ? ? }
? ? ? ? }, 2000, 40);
//2000表示第一次執(zhí)行任務(wù)延遲時(shí)間,40表示以后每隔多長(zhǎng)時(shí)間執(zhí)行一次run里面的任務(wù)ScheduledThreadPoolExecutor的使用:
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
?
/**
?* 設(shè)備在線延時(shí)檢查檢查
?*/
@Slf4j
public class DelayedCheckDeviceSchedule {
?
? public static final Integer CONNECT_TIME_OUT = 10000;
?
//引用的業(yè)務(wù)層
? private ITblBaseService baseService = SpringContextHolder.getBean(ITblBaseService .class);
?
? private ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = null;
?
? /**
? ?* 需要延時(shí)檢查的設(shè)備狀態(tài)的設(shè)備id集合
? ?*/
? public static Set<String> deviceSet = new HashSet<>();
?
? /**
? ?* 當(dāng)前執(zhí)行點(diǎn)
? ?*/
? private AtomicInteger currentAtomicInteger = new AtomicInteger(1);
?
?
? /**
? ?* 初始化任務(wù)
? ?* @param delay 延遲幾秒執(zhí)行
? ?* @param checkCount 需要檢測(cè)的次數(shù)
? ?* @param deviceId 設(shè)備Id
? ?* @param deviceType 設(shè)備類(lèi)型
? ?* @return
? ?*/
? public boolean init(long delay, int checkCount, String deviceId, String deviceType) {
? ? log.info("第一次初始化時(shí)間"+deviceId+":"+System.currentTimeMillis());
? ? if (deviceSet.add(deviceId) && deviceConnectModel!=null) {
? ? ? this.scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(4);
? ? ? this.scheduledThreadPoolExecutor.schedule(new Runnable() {
? ? ? ? @Override
? ? ? ? public void run() {
? ? ? ? ? executor(delay, checkCount, deviceId, deviceType);
? ? ? ? }
? ? ? }, delay, TimeUnit.SECONDS);
? ? ? return true;
? ? }
? ? return false;
? }
?
? /**
? ?* 執(zhí)行體
? ?*/
? private void executor(long delay, int checkCount, String deviceId, String deviceType) {
? ? log.info("第"+currentAtomicInteger.get()+"執(zhí)行時(shí)間"+deviceId+":"+System.currentTimeMillis());
? ? if (deviceSet.contains(deviceId) && currentAtomicInteger.get() < (checkCount+1)) {
? ? ? //執(zhí)行邏輯
? ? ? ? ??
? ? ? ?//當(dāng)滿(mǎn)足條件時(shí),停止任務(wù)
? ? ? ? if(currentAtomicInteger.get()==checkCount){?
? ? ? ? ? //需要處理的邏輯
?
? ? ? ? ? //停止任務(wù)
? ? ? ? ? this.scheduledThreadPoolExecutor.shutdownNow();
? ? ? ? ? deviceSet.remove(deviceId);
? ? ? ? }else {
? ? ? ? ? //下一次執(zhí)行
? ? ? ? ? currentAtomicInteger.getAndIncrement();
? ? ? ? ? this.scheduledThreadPoolExecutor.schedule(new Runnable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? executor(delay, checkCount, deviceId, deviceType);
? ? ? ? ? ? }
? ? ? ? ? }, delay, TimeUnit.SECONDS);
? ? ?
? ? ? ?}
? ? } else {
? ? ? this.scheduledThreadPoolExecutor.shutdownNow();
? ? ? this.scheduledThreadPoolExecutor = null;
? ? }
? }
?
? /**
? ?* 停止檢測(cè)任務(wù)
? ?* @param deviceId
? ?* @return
? ?*/
? public static boolean stop(String deviceId) {
? ? return deviceSet.remove(deviceId);
? }
?
}在需要周期性檢查的時(shí)候引入:
DelayedCheckDeviceSchedule delayedCheckDeviceSchedule = new DelayedCheckDeviceSchedule(); delayedCheckDeviceSchedule.init(10, 3, panModel.getDeviceId(), "pan");
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java實(shí)現(xiàn)并發(fā)執(zhí)行定時(shí)任務(wù)并手動(dòng)控制開(kāi)始結(jié)束
- Java定時(shí)任務(wù)取消的示例代碼
- Java定時(shí)任務(wù)Timer、TimerTask與ScheduledThreadPoolExecutor詳解
- Java?@Scheduled定時(shí)任務(wù)不執(zhí)行解決辦法
- Java實(shí)現(xiàn)定時(shí)任務(wù)的方法總結(jié)
- Java?Elastic-Job分布式定時(shí)任務(wù)使用方法介紹
- java8中定時(shí)任務(wù)最佳實(shí)現(xiàn)方式(實(shí)現(xiàn)原理)
相關(guān)文章
java實(shí)現(xiàn)全局監(jiān)聽(tīng)鍵盤(pán)詳解
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)全局監(jiān)聽(tīng)鍵盤(pán)的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),具有一定的學(xué)習(xí)價(jià)值,感興趣的小伙伴可以了解下2024-01-01
java基礎(chǔ)學(xué)習(xí)JVM中GC的算法
這篇文章主要介紹了java基礎(chǔ)學(xué)習(xí)JVM中GC的算法,通過(guò)圖文加深對(duì)GC算法思路的理解。2017-11-11
springboot2中HikariCP連接池的相關(guān)配置問(wèn)題
這篇文章主要介紹了springboot2中HikariCP連接池的相關(guān)配置問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
nacos將服務(wù)注冊(cè)到不同的命名空間下問(wèn)題
Nacos是SpringCloudAlibaba架構(gòu)中最重要的組件,提供注冊(cè)中心、配置中心和動(dòng)態(tài)DNS服務(wù)三大功能,如果需要配置多個(gè)數(shù)據(jù)庫(kù)適配的環(huán)境,啟動(dòng)服務(wù)時(shí)需要將服務(wù)注冊(cè)到不同的命名空間下,并配置新部署的網(wǎng)關(guān)服務(wù)ip和端口或者域名2024-12-12
spring data jpa 創(chuàng)建方法名進(jìn)行簡(jiǎn)單查詢(xún)方式
這篇文章主要介紹了spring data jpa 創(chuàng)建方法名進(jìn)行簡(jiǎn)單查詢(xún)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02
File.createTempFile創(chuàng)建臨時(shí)文件的示例詳解
這篇文章主要介紹了File.createTempFile創(chuàng)建臨時(shí)文件的示例詳解,在默認(rèn)臨時(shí)文件目錄中創(chuàng)建一個(gè)空文件,使用給定前綴和后綴生成其名稱(chēng)。 如果感興趣來(lái)了解一下2020-07-07
如何使用Sentry 監(jiān)控你的Spring Boot應(yīng)用
這篇文章主要介紹了如何使用Sentry 監(jiān)控你的Spring Boot應(yīng)用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
對(duì)比Java講解Kotlin中?.與!!.的區(qū)別
這篇文章主要給大家介紹了關(guān)于對(duì)比Java,實(shí)例講解Kotlin中?.與!!.的區(qū)別,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用kotlin具有一定參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06

