国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開發(fā)設(shè)計(jì) > 正文

java Timer(定時(shí)調(diào)用、實(shí)現(xiàn)固定時(shí)間執(zhí)行)

2019-11-15 01:02:47
字體:
供稿:網(wǎng)友
java Timer(定時(shí)調(diào)用、實(shí)現(xiàn)固定時(shí)間執(zhí)行)

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

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

Timer timer = new Timer();timer.schedule(new TimerTask() {        public void run() {            System.out.

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

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

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

    public void schedule(TimerTask task, long delay)

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

public void schedule(TimerTask task, Date time)

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

4、和上一個(gè)方法類似,唯一的區(qū)別就是傳入的第二個(gè)參數(shù)為第一次調(diào)度的時(shí)間。
public void schedule(TimerTask task, Date firstTime, long period)

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

6、方法同上,唯一的區(qū)別就是第一次調(diào)度時(shí)間設(shè)置為一個(gè)Date時(shí)間,而不是當(dāng)前時(shí)間的一個(gè)時(shí)間片,我們?cè)谠创a中會(huì)詳細(xì)說明這些內(nèi)容。
public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period)


源碼部分首先看Timer的構(gòu)造方法有幾種:構(gòu)造方法1:無(wú)參構(gòu)造方法,簡(jiǎn)單通過Tiemer為前綴構(gòu)造一個(gè)線程名稱:
public Timer() {    this("Timer-" + serialNumber());}

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

構(gòu)造方法2:傳入了是否為后臺(tái)線程,后臺(tái)線程當(dāng)且僅當(dāng)進(jìn)程結(jié)束時(shí),自動(dòng)注銷掉。
public Timer(boolean isDaemon) {    this("Timer-" + serialNumber(), isDaemon);}

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

public Timer(String name, boolean isDaemon) {      thread.setName(name);      thread.setDaemon(isDaemon);      thread.start();  }

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

private TimerThread thread = new TimerThread(queue);

  而定義TimerThread部分的是:
而定義TimerThread部分的是:

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

  接下來看下有那些屬性  除了上面提到的thread,還有一個(gè)很重要的屬性是:
private TaskQueue queue = new TaskQueue();

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

  里面還有一個(gè)屬性是:threadReaper, 它是Object類型,只是重寫了finalize方法而已,是為了垃圾回收的時(shí)候,將相應(yīng)的信息回收掉,做GC的回補(bǔ),也就是當(dāng)timer線程由于某種 原因死掉了,而未被cancel,里面的隊(duì)列中的信息需要清空掉,不過我們通常是不會(huì)考慮這個(gè)方法的,所以知道java寫這個(gè)方法是干什么的就行了。  接下來看調(diào)度方法的實(shí)現(xiàn):  對(duì)于上面6個(gè)調(diào)度方法,我們不做一一列舉,為什么等下你就知道了:

  來看下方法:

public void schedule(TimerTask task, long delay)

  的源碼如下:

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

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

  再看看方法:
public void schedule(TimerTask task, long delay,long period)

  源碼為:

public void schedule(TimerTask task, long delay, long period) {        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)度時(shí)候的區(qū)別是增加了傳入的period,而第一個(gè)傳入的是0,所以確定這個(gè)參數(shù)為時(shí)間片, 而不是次數(shù),注意這個(gè)里的period加了一個(gè)負(fù)數(shù),也就是取反,也就是我們開始傳入1000,在調(diào)用sched的時(shí)候會(huì)變成-1000,其實(shí)最終閱讀完 源碼后你會(huì)發(fā)現(xiàn)這個(gè)算是老外對(duì)于一種數(shù)字的理解,而并非有什么特殊的意義,所以閱讀源碼的時(shí)候也有這些困難所在。

  最后再看個(gè)方法是:
public void scheduleAtFixedRate(TimerTasktask,long delay,long period)

  源碼為:

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

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

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

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

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

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

   


實(shí)踐部分:1、通過繼承TimerTask的方式實(shí)現(xiàn)  必須重寫run方法.
public class MyTask extends TimerTask{    @Override    public void run()    {        SimpleDateFormat sdf = null;        sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");        System.out.println("當(dāng)前時(shí)間:" + sdf.format(new Date()));            }    }

public class TestTask{    public static void main(String[] args)    {        Timer t = new Timer(); // 建立Timer對(duì)象        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)部類實(shí)現(xiàn)

Timer timer = new Timer();          timer.scheduleAtFixedRate(new TimerTask() {                  public void run() {                                          System.out.println("abc");                  }          }, 1000 , 1000);

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


發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 融水| 岳西县| 昌江| 英山县| 巴彦县| 海门市| 库尔勒市| 昂仁县| 驻马店市| 汝南县| 永兴县| 长沙县| 秭归县| 香河县| 都昌县| 宁远县| 醴陵市| 甘孜| 札达县| 讷河市| 岳阳县| 丰城市| 巩留县| 峡江县| 兰考县| 新巴尔虎左旗| 台山市| 安阳县| 宁化县| 陆丰市| 辰溪县| 卫辉市| 探索| 丹寨县| 禄劝| 新化县| 沙洋县| 松潘县| 新营市| 西丰县| 公主岭市|