日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java定时执行一次_java Timer(定时调用、实现固定时间执行)

發(fā)布時間:2023/12/10 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java定时执行一次_java Timer(定时调用、实现固定时间执行) 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

最近需要用到定時調(diào)用的功能。可以通過java的Timer類來進(jìn)行定時調(diào)用,下面是有關(guān)Timer的一些相關(guān)知識。

其實就Timer來講就是一個調(diào)度器,而TimerTask呢只是一個實現(xiàn)了run方法的一個類,而具體的TimerTask需要由你自己來實現(xiàn),例如這樣:

Timer timer = newTimer();

timer.schedule(newTimerTask() {public voidrun() {

System.out.println("11232");

}

},200000 , 1000);

這里直接實現(xiàn)一個TimerTask(當(dāng)然,你可以實現(xiàn)多個TimerTask,多個TimerTask可以被一個Timer會被分配到多個 Timer中被調(diào)度,后面會說到Timer的實現(xiàn)機(jī)制就是說內(nèi)部的調(diào)度機(jī)制),然后編寫run方法,20s后開始執(zhí)行,每秒執(zhí)行一次,當(dāng)然你通過一個 timer對象來操作多個timerTask,其實timerTask本身沒什么意義,只是和timer集合操作的一個對象,實現(xiàn)它就必然有對應(yīng)的run 方法,以被調(diào)用,他甚至于根本不需要實現(xiàn)Runnable,因為這樣往往混淆視聽了,為什么呢?也是本文要說的重點。

在說到timer的原理時,我們先看看Timer里面的一些常見方法:

1、這個方法是調(diào)度一個task,經(jīng)過delay(ms)后開始進(jìn)行調(diào)度,僅僅調(diào)度一次。

public void schedule(TimerTask task, long delay)

2、在指定的時間點time上調(diào)度一次。

public void schedule(TimerTask task, Date time)

3、這個方法是調(diào)度一個task,在delay(ms)后開始調(diào)度,每次調(diào)度完后,最少等待period(ms)后才開始調(diào)度。

public void schedule(TimerTask task, long delay, long period)

4、和上一個方法類似,唯一的區(qū)別就是傳入的第二個參數(shù)為第一次調(diào)度的時間。

public void schedule(TimerTask task, Date firstTime, long period)

5、調(diào)度一個task,在delay(ms)后開始調(diào)度,然后每經(jīng)過period(ms)再次調(diào)度,貌似和方法:schedule是一樣的,其實不然,后面你會根據(jù)源碼看到,schedule在計算下一次執(zhí)行的時間的時候,是通過當(dāng)前時間(在任務(wù)執(zhí)行前得到) + 時間片,而scheduleAtFixedRate方法是通過當(dāng)前需要執(zhí)行的時間(也就是計算出現(xiàn)在應(yīng)該執(zhí)行的時間)+ 時間片,前者是運行的實際時間,而后者是理論時間點,例如:schedule時間片是5s,那么理論上會在5、10、15、20這些時間片被調(diào)度,但是如果由于某些CPU征用導(dǎo)致未被調(diào)度,假如等到第8s才被第一次調(diào)度,那么schedule方法計算出來的下一次時間應(yīng)該是第13s而不是第10s,這樣有可能下次就越到20s后而被少調(diào)度一次或多次,而scheduleAtFixedRate方法就是每次理論計算出下一次需要調(diào)度的時間用以排序,若第8s被調(diào)度,那么計算出應(yīng)該是第10s,所以它距離當(dāng)前時間是2s,那么再調(diào)度隊列排序中,會被優(yōu)先調(diào)度,那么就盡量減少漏掉調(diào)度的情況。

public void scheduleAtFixedRate(TimerTask task, long delay, long period)

6、方法同上,唯一的區(qū)別就是第一次調(diào)度時間設(shè)置為一個Date時間,而不是當(dāng)前時間的一個時間片,我們在源碼中會詳細(xì)說明這些內(nèi)容。

public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period)

源碼部分

首先看Timer的構(gòu)造方法有幾種:

構(gòu)造方法1:無參構(gòu)造方法,簡單通過Tiemer為前綴構(gòu)造一個線程名稱:

publicTimer() {this("Timer-" +serialNumber());

}

創(chuàng)建的線程不為主線程,則主線程結(jié)束后,timer自動結(jié)束,而無需使用cancel來完成對timer的結(jié)束。

構(gòu)造方法2:傳入了是否為后臺線程,后臺線程當(dāng)且僅當(dāng)進(jìn)程結(jié)束時,自動注銷掉。

public Timer(booleanisDaemon) {this("Timer-" +serialNumber(), isDaemon);

}

另外兩個構(gòu)造方法負(fù)責(zé)傳入名稱和將timer啟動:

public Timer(String name, booleanisDaemon) {

thread.setName(name);

thread.setDaemon(isDaemon);

thread.start();

}

這里有一個thread,這個thread很明顯是一個線程,被包裝在了Timer類中,我們看下這個thread的定義是:

private TimerThread thread = new TimerThread(queue);

而定義TimerThread部分的是:

而定義TimerThread部分的是:

看到這里知道了,Timer內(nèi)部包裝了一個線程,用來做獨立于外部線程的調(diào)度,而TimerThread是一個default類型的,默認(rèn)情況下是引用不到的,是被Timer自己所使用的。

接下來看下有那些屬性

除了上面提到的thread,還有一個很重要的屬性是:

private TaskQueue queue = new TaskQueue();

看名字就知道是一個隊列,隊列里面可以先猜猜看是什么,那么大概應(yīng)該是我要調(diào)度的任務(wù)吧,先記錄下了,接下來繼續(xù)向下看:

里面還有一個屬性是:threadReaper, 它是Object類型,只是重寫了finalize方法而已,是為了垃圾回收的時候,將相應(yīng)的信息回收掉,做GC的回補,也就是當(dāng)timer線程由于某種 原因死掉了,而未被cancel,里面的隊列中的信息需要清空掉,不過我們通常是不會考慮這個方法的,所以知道java寫這個方法是干什么的就行了。

接下來看調(diào)度方法的實現(xiàn):

對于上面6個調(diào)度方法,我們不做一一列舉,為什么等下你就知道了:

來看下方法:

public void schedule(TimerTask task, long delay)

的源碼如下:

1 public void schedule(TimerTask task, longdelay) {2 if (delay < 0)3 throw new IllegalArgumentException("Negative delay.");4 sched(task, System.currentTimeMillis()+delay, 0);5 }

這里調(diào)用了另一個方法,將task傳入,第一個參數(shù)傳入System.currentTimeMillis()+delay可見為第一次需要執(zhí)行的時間的 時間點了(如果傳入Date,就是對象.getTime()即可,所以傳入Date的幾個方法就不用多說了),而第三個參數(shù)傳入了0,這里可以猜下要么是 時間片,要么是次數(shù)啥的,不過等會就知道是什么了;另外關(guān)于方法:sched的內(nèi)容我們不著急去看他,先看下重載的方法中是如何做的

再看看方法:

public void schedule(TimerTask task, long delay,long period)

源碼為:

public void schedule(TimerTask task, long delay, longperiod) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");if (period <= 0)throw new IllegalArgumentException("Non-positive period.");

sched(task, System.currentTimeMillis()+delay, -period);

}

看來也調(diào)用了方法sched來完成調(diào)度,和上面的方法唯一的調(diào)度時候的區(qū)別是增加了傳入的period,而第一個傳入的是0,所以確定這個參數(shù)為時間片, 而不是次數(shù),注意這個里的period加了一個負(fù)數(shù),也就是取反,也就是我們開始傳入1000,在調(diào)用sched的時候會變成-1000,其實最終閱讀完 源碼后你會發(fā)現(xiàn)這個算是老外對于一種數(shù)字的理解,而并非有什么特殊的意義,所以閱讀源碼的時候也有這些困難所在。

最后再看個方法是:

public void scheduleAtFixedRate(TimerTasktask,long delay,long period)

源碼為:

public void scheduleAtFixedRate(TimerTask task, long delay, longperiod) {if (delay < 0)throw new IllegalArgumentException("Negative delay.");if (period <= 0)throw new IllegalArgumentException("Non-positive period.");

sched(task, System.currentTimeMillis()+delay, period);

}

唯一的區(qū)別就是在period沒有取反,其實你最終閱讀完源碼,上面的取反沒有什么特殊的意義,老外不想增加一個參數(shù)來表示 scheduleAtFixedRate,而scheduleAtFixedRate和schedule的大部分邏輯代碼一致,因此用了參數(shù)的范圍來作為 區(qū)分方法,也就是當(dāng)你傳入的參數(shù)不是正數(shù)的時候,你調(diào)用schedule方法正好是得到scheduleAtFixedRate的功能,而調(diào)用 scheduleAtFixedRate方法的時候得到的正好是schedule方法的功能,呵呵,這些討論沒什么意義,討論實質(zhì)和重點:

來看sched方法的實現(xiàn)體:

private void sched(TimerTask task, long time, longperiod) {if (time < 0)throw new IllegalArgumentException("Illegal execution time.");synchronized(queue) {if (!thread.newTasksMayBeScheduled)throw new IllegalStateException("Timer already cancelled.");synchronized(task.lock) {if (task.state !=TimerTask.VIRGIN)throw newIllegalStateException("Task already scheduled or cancelled");

task.nextExecutionTime=time;

task.period=period;

task.state=TimerTask.SCHEDULED;

}

queue.add(task);if (queue.getMin() ==task)

queue.notify();

}

}

queue為一個隊列,我們先不看他數(shù)據(jù)結(jié)構(gòu),看到他在做這個操作的時候,發(fā)生了同步,所以在timer級別,這個是線程安全的,最后將task相關(guān)的參數(shù)賦值,主要包含nextExecutionTime(下一次執(zhí)行時間),period(時間片),state(狀態(tài)),然后將它放入queue隊列中,做一次notify操作,為什么要做notify操作呢?看了后面的代碼你就知道了。

簡言之,這里就是講task放入隊列queue的過程,此時,你可能對queue的結(jié)構(gòu)有些興趣,那么我們先來看看queue屬性的結(jié)構(gòu)TaskQueue:

classTaskQueue {private TimerTask[] queue = new TimerTask[128];private int size = 0;

可見,TaskQueue的結(jié)構(gòu)很簡單,為一個數(shù)組,加一個size,有點像ArrayList,是不是長度就128呢,當(dāng)然不 是,ArrayList可以擴(kuò)容,它可以,只是會造成內(nèi)存拷貝而已,所以一個Timer來講,只要內(nèi)部的task個數(shù)不超過128是不會造成擴(kuò)容的;內(nèi)部 提供了add(TimerTask)、size()、getMin()、get(int)、removeMin()、quickRemove(int)、 rescheduleMin(long newTime)、isEmpty()、clear()、fixUp()、fixDown()、heapify();

實踐部分:

1、通過繼承TimerTask的方式實現(xiàn)

必須重寫run方法.

public class MyTask extendsTimerTask

{

@Overridepublic voidrun()

{

SimpleDateFormat sdf= null;

sdf= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

System.out.println("當(dāng)前時間:" + sdf.format(newDate()));

}

}

public classTestTask

{public static voidmain(String[] args)

{

Timer t= new Timer(); //建立Timer對象

MyTask task = new MyTask(); //定義任務(wù)

t.schedule(task, 1000,2000);//設(shè)置任務(wù)的執(zhí)行,1秒后開始,每2秒執(zhí)行一次

Calendar cal=Calendar.getInstance();

cal.set(Calendar.MINUTE,30);

t.schedule(task, cal.getTime() ,2000);

}

}

2、通過匿名內(nèi)部類實現(xiàn)

Timer timer = newTimer();

timer.scheduleAtFixedRate(newTimerTask() {public voidrun() {

System.out.println("abc");

}

},1000 , 1000);

致謝:感謝您的耐心閱讀!

總結(jié)

以上是生活随笔為你收集整理的java定时执行一次_java Timer(定时调用、实现固定时间执行)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。