黄色网址大全免费-黄色网址你懂得-黄色网址你懂的-黄色网址有那些-免费超爽视频-免费大片黄国产在线观看

專注Java教育14年 全國(guó)咨詢/投訴熱線:400-8080-105
動(dòng)力節(jié)點(diǎn)LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁(yè) hot資訊 Spring定時(shí)器詳解

Spring定時(shí)器詳解

更新時(shí)間:2021-12-10 10:10:58 來(lái)源:動(dòng)力節(jié)點(diǎn) 瀏覽3732次

Spring定時(shí)器是什么?小編來(lái)告訴大家。

1. 定時(shí)器——基礎(chǔ)

Timer和TimerTask是用于在后臺(tái)線程中調(diào)度任務(wù)的 java util 類。簡(jiǎn)而言之 - TimerTask是要執(zhí)行的任務(wù),Timer是調(diào)度程序。

2. 安排一次任務(wù)

(1)在給定的延遲之后

讓我們首先在Timer的幫助下簡(jiǎn)單地運(yùn)行單個(gè)任務(wù):

@Test
public void givenUsingTimer_whenSchedulingTaskOnce_thenCorrect() {
    TimerTask task = new TimerTask() {
        public void run() {
            System.out.println("Task performed on: " + new Date() + "n" +
              "Thread's name: " + Thread.currentThread().getName());
        }
    };
    Timer timer = new Timer("Timer");    
    long delay = 1000L;
    timer.schedule(task, delay);
}

現(xiàn)在,這在一定的延遲后執(zhí)行任務(wù),作為schedule()方法的第二個(gè)參數(shù)給出 。我們將在下一節(jié)看到如何在給定的日期和時(shí)間安排任務(wù)。

請(qǐng)注意,如果我們正在運(yùn)行這是一個(gè) JUnit 測(cè)試,我們應(yīng)該添加一個(gè)Thread.sleep(delay * 2)調(diào)用,以允許 Timer 的線程在 Junit 測(cè)試停止執(zhí)行之前運(yùn)行任務(wù)。

(2)在給定的日期和時(shí)間

現(xiàn)在,讓我們看看Timer#schedule(TimerTask, Date)方法,它接受一個(gè)Date而不是long作為它的第二個(gè)參數(shù),允許我們?cè)谀硞€(gè)時(shí)刻安排任務(wù),而不是在延遲之后。

這一次,讓我們假設(shè)我們有一個(gè)舊的遺留數(shù)據(jù)庫(kù),我們希望將其數(shù)據(jù)遷移到一個(gè)具有更好模式的新數(shù)據(jù)庫(kù)中。

我們可以創(chuàng)建一個(gè)DatabaseMigrationTask類來(lái)處理該遷移:

public class DatabaseMigrationTask extends TimerTask {
    private List<String> oldDatabase;
    private List<String> newDatabase;
    public DatabaseMigrationTask(List<String> oldDatabase, List<String> newDatabase) {
        this.oldDatabase = oldDatabase;
        this.newDatabase = newDatabase;
    }
    @Override
    public void run() {
        newDatabase.addAll(oldDatabase);
    }
}

為簡(jiǎn)單起見,我們代表著兩個(gè)數(shù)據(jù)庫(kù)通過列表的 字符串。簡(jiǎn)單地說(shuō),我們的遷移包括將第一個(gè)列表中的數(shù)據(jù)放入第二個(gè)列表中。

要在所需的時(shí)刻執(zhí)行此遷移,我們必須使用schedule ()方法的重載版本 :

List<String> oldDatabase = Arrays.asList("Harrison Ford", "Carrie Fisher", "Mark Hamill");
List<String> newDatabase = new ArrayList<>();
LocalDateTime twoSecondsLater = LocalDateTime.now().plusSeconds(2);
Date twoSecondsLaterAsDate = Date.from(twoSecondsLater.atZone(ZoneId.systemDefault()).toInstant());
new Timer().schedule(new DatabaseMigrationTask(oldDatabase, newDatabase), twoSecondsLaterAsDate);

如我們所見,我們將遷移任務(wù)以及執(zhí)行日期提供給 schedule()方法。

然后,在twoSecondsLater指示的時(shí)間執(zhí)行遷移:

while (LocalDateTime.now().isBefore(twoSecondsLater)) {
    assertThat(newDatabase).isEmpty();
    Thread.sleep(500);
}
assertThat(newDatabase).containsExactlyElementsOf(oldDatabase);

雖然我們?cè)谶@一刻之前,遷移不會(huì)發(fā)生。

3. 安排一個(gè)可重復(fù)的任務(wù)

既然我們已經(jīng)介紹了如何安排任務(wù)的單次執(zhí)行,讓我們看看如何處理可重復(fù)的任務(wù)。

再一次,Timer 類提供了多種可能性 :我們可以設(shè)置重復(fù)以觀察固定延遲或固定速率。

固定延遲意味著執(zhí)行將在上次執(zhí)行開始后的一段時(shí)間開始,即使它被延遲(因此本身被延遲)。

假設(shè)我們想每?jī)擅氚才乓淮稳蝿?wù),第一次執(zhí)行需要一秒鐘,第二次執(zhí)行需要兩秒鐘但延遲一秒鐘。然后,第三次執(zhí)行將在第五秒開始:

0s     1s    2s     3s           5s
|--T1--|
|-----2s-----|--1s--|-----T2-----|
|-----2s-----|--1s--|-----2s-----|--T3--|

另一方面,固定速率意味著每次執(zhí)行都將遵守初始計(jì)劃,無(wú)論之前的執(zhí)行是否被延遲。

讓我們重用前面的例子,在固定速率下,第二個(gè)任務(wù)將在三秒后開始(因?yàn)檠舆t)。但是,四秒后的第三個(gè)(尊重每?jī)擅雸?zhí)行一次的初始計(jì)劃):

0s     1s    2s     3s    4s
|--T1--|       
|-----2s-----|--1s--|-----T2-----|
|-----2s-----|-----2s-----|--T3--|

這兩個(gè)原理都講完了,讓我們看看如何使用它們。

為了使用固定延遲調(diào)度,schedule()方法還有兩個(gè)重載,每個(gè)重載都有一個(gè)額外的參數(shù),以毫秒為單位說(shuō)明周期性。

為什么有兩個(gè)重載?因?yàn)槿匀挥锌赡茉谀硞€(gè)時(shí)刻或在某個(gè)延遲之后開始任務(wù)。

至于固定速率調(diào)度,我們有兩個(gè) scheduleAtFixedRate()方法也以毫秒為單位。同樣,我們有一種方法可以在給定的日期和時(shí)間啟動(dòng)任務(wù),另一種方法可以在給定的延遲后啟動(dòng)它。

還值得一提的是,如果任務(wù)花費(fèi)的時(shí)間比執(zhí)行周期長(zhǎng),無(wú)論我們使用固定延遲還是固定速率,它都會(huì)延遲整個(gè)執(zhí)行鏈。

這兩個(gè)原理都講完了,讓我們看看如何使用它們。

為了使用固定延遲調(diào)度,schedule()方法還有兩個(gè)重載,每個(gè)重載都有一個(gè)額外的參數(shù),以毫秒為單位說(shuō)明周期性。

為什么有兩個(gè)重載?因?yàn)槿匀挥锌赡茉谀硞€(gè)時(shí)刻或在某個(gè)延遲之后開始任務(wù)。

至于固定速率調(diào)度,我們有兩個(gè) scheduleAtFixedRate()方法也以毫秒為單位。同樣,我們有一種方法可以在給定的日期和時(shí)間啟動(dòng)任務(wù),另一種方法可以在給定的延遲后啟動(dòng)它。

還值得一提的是,如果任務(wù)花費(fèi)的時(shí)間比執(zhí)行周期長(zhǎng),無(wú)論我們使用固定延遲還是固定速率,它都會(huì)延遲整個(gè)執(zhí)行鏈。

(1)固定延遲

現(xiàn)在,讓我們假設(shè)我們想要實(shí)施一個(gè)時(shí)事通訊系統(tǒng),每周向我們的關(guān)注者發(fā)送一封電子郵件。在這種情況下,重復(fù)性任務(wù)似乎很理想。

所以,讓我們每秒安排一次時(shí)事通訊,這基本上是垃圾郵件,但由于發(fā)送是假的,我們很高興!

讓我們首先設(shè)計(jì)一個(gè) NewsletterTask:

public class NewsletterTask extends TimerTask {
    @Override
    public void run() {
        System.out.println("Email sent at: " 
          + LocalDateTime.ofInstant(Instant.ofEpochMilli(scheduledExecutionTime()), 
          ZoneId.systemDefault()));
    }
}

每次執(zhí)行時(shí),任務(wù)都會(huì)打印其計(jì)劃時(shí)間,我們使用TimerTask#scheduledExecutionTime()方法收集這些時(shí)間。

那么,如果我們想在固定延遲模式下每秒調(diào)度這個(gè)任務(wù)怎么辦?我們將不得不使用我們之前討論過的schedule()的重載版本:

new Timer().schedule(new NewsletterTask(), 0, 1000);
for (int i = 0; i < 3; i++) {
    Thread.sleep(1000);
}

當(dāng)然,我們只對(duì)少數(shù)情況進(jìn)行測(cè)試:

Email sent at: 2020-01-01T10:50:30.860
Email sent at: 2020-01-01T10:50:31.860
Email sent at: 2020-01-01T10:50:32.861
Email sent at: 2020-01-01T10:50:33.861

正如我們所看到的,每次執(zhí)行之間至少有 1 秒的時(shí)間,但它們有時(shí)會(huì)延遲一毫秒。這種現(xiàn)象是由于我們決定使用固定延遲重復(fù)。

(2)固定利率

現(xiàn)在,如果我們使用固定速率重復(fù)呢?然后我們將不得不使用 scheduleAtFixedRate()方法:

new Timer().scheduleAtFixedRate(new NewsletterTask(), 0, 1000);
for (int i = 0; i < 3; i++) {
    Thread.sleep(1000);
}

這一次,執(zhí)行不會(huì)被前面的延遲:

Email sent at: 2020-01-01T10:55:03.805
Email sent at: 2020-01-01T10:55:04.805
Email sent at: 2020-01-01T10:55:05.805
Email sent at: 2020-01-01T10:55:06.805

(3)安排每日任務(wù)

接下來(lái),讓我們每天運(yùn)行一次任務(wù):

@Test
public void givenUsingTimer_whenSchedulingDailyTask_thenCorrect() {
    TimerTask repeatedTask = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
        }
    };
    Timer timer = new Timer("Timer");    
    long delay = 1000L;
    long period = 1000L * 60L * 60L * 24L;
    timer.scheduleAtFixedRate(repeatedTask, delay, period);
}

4.取消定時(shí)器和TimerTask

可以通過以下幾種方式取消任務(wù)的執(zhí)行:

(1)在運(yùn)行中取消TimerTask

通過在TimerTask本身的run()方法實(shí)現(xiàn)中調(diào)用TimerTask.cancel()方法:

@Test
public void givenUsingTimer_whenCancelingTimerTask_thenCorrect()
  throws InterruptedException {
    TimerTask task = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
            cancel();
        }
    };
    Timer timer = new Timer("Timer");    
    timer.scheduleAtFixedRate(task, 1000L, 1000L);    
    Thread.sleep(1000L * 2);
}

(2)取消定時(shí)器

通過在Timer對(duì)象上調(diào)用Timer.cancel()方法:

@Test
public void givenUsingTimer_whenCancelingTimer_thenCorrect() 
  throws InterruptedException {
    TimerTask task = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
        }
    };
    Timer timer = new Timer("Timer");    
    timer.scheduleAtFixedRate(task, 1000L, 1000L);    
    Thread.sleep(1000L * 2); 
    timer.cancel(); 
}

(3)停止內(nèi)部運(yùn)行的TimerTask的線程

您還可以在任務(wù)的run方法中停止線程,從而取消整個(gè)任務(wù):

@Test
public void givenUsingTimer_whenStoppingThread_thenTimerTaskIsCancelled() 
  throws InterruptedException {
    TimerTask task = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
            // TODO: stop the thread here
        }
    };
    Timer timer = new Timer("Timer");
    timer.scheduleAtFixedRate(task, 1000L, 1000L);    
    Thread.sleep(1000L * 2); 
}

注意run實(shí)現(xiàn)中的 TODO 指令——為了運(yùn)行這個(gè)簡(jiǎn)單的例子,我們需要實(shí)際停止線程。

在現(xiàn)實(shí)世界的自定義線程實(shí)現(xiàn)中,應(yīng)該支持停止線程,但在這種情況下,我們可以忽略棄用并在 Thread 類本身上使用簡(jiǎn)單的停止API。

5.定時(shí)器vs ExecutorService

你也可以很好地利用一個(gè)ExecutorService來(lái)調(diào)度定時(shí)器任務(wù),而不是使用定時(shí)器。

以下是如何以指定的時(shí)間間隔運(yùn)行重復(fù)任務(wù)的快速示例:

@Test
public void givenUsingExecutorService_whenSchedulingRepeatedTask_thenCorrect() 
  throws InterruptedException {
    TimerTask repeatedTask = new TimerTask() {
        public void run() {
            System.out.println("Task performed on " + new Date());
        }
    };
    ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    long delay  = 1000L;
    long period = 1000L;
    executor.scheduleAtFixedRate(repeatedTask, delay, period, TimeUnit.MILLISECONDS);
    Thread.sleep(delay + period * 3);
    executor.shutdown();
}

那么Timer和ExecutorService解決方案的主要區(qū)別是什么:

定時(shí)器可以對(duì)系統(tǒng)時(shí)鐘的變化敏感;ScheduledThreadPoolExecutor不是

定時(shí)器只有一個(gè)執(zhí)行線程;ScheduledThreadPoolExecutor可以配置任意數(shù)量的線程

TimerTask 中拋出的運(yùn)行時(shí)異常會(huì)殺死線程,因此后續(xù)的計(jì)劃任務(wù)不會(huì)繼續(xù)運(yùn)行;with ScheduledThreadExecutor - 當(dāng)前任務(wù)將被取消,但其余的將繼續(xù)運(yùn)行

6.結(jié)論

本教程說(shuō)明了可以利用Java 中內(nèi)置的簡(jiǎn)單而靈活的Timer和TimerTask基礎(chǔ)結(jié)構(gòu)來(lái)快速安排任務(wù)的多種方法。當(dāng)然,如果您需要,Java 世界中還有更復(fù)雜和完整的解決方案——例如Quartz 庫(kù)——但這是一個(gè)非常好的起點(diǎn)。

這些示例的實(shí)現(xiàn)可以在GitHub項(xiàng)目中找到——這是一個(gè)基于 Eclipse 的項(xiàng)目,因此應(yīng)該很容易導(dǎo)入和運(yùn)行。

提交申請(qǐng)后,顧問老師會(huì)電話與您溝通安排學(xué)習(xí)

  • 全國(guó)校區(qū) 2025-05-15 搶座中
  • 全國(guó)校區(qū) 2025-06-05 搶座中
  • 全國(guó)校區(qū) 2025-06-26 搶座中
免費(fèi)課程推薦 >>
技術(shù)文檔推薦 >>
主站蜘蛛池模板: 亚州va| 亚洲天堂精品在线观看 | 日本三级全黄三级三级三级口周 | 伊人网网 | 婷婷开心激情网 | 人人爱人人插 | 亚洲日本中文字幕在线 | 午夜在线不卡 | 亚洲国产成人久久综合一 | 欧美精品亚洲精品日韩专区 | 在线亚洲观看 | xxxx日日摸夜夜添夜夜添视频 | 日日摸夜夜添免费毛片小说 | 欧美成人性视频播放 | 一级一片一_级一片一 | 一区二区三区四区欧美 | 国产成人精品亚洲一区 | 久久综合精品不卡一区二区 | 亚洲乱乱| 国产成人爱情动作片在线观看 | 99视频精品免视3 | 久久久久夜夜夜精品国产 | 草碰视频 | 国产精品嫩草影院奶水 | 成人a免费视频播放 | 羞羞视频免费网站欧美 | 亚洲综合国产 | 天天碰夜夜 | 欧美午夜成年片在线观看 | 特级黄色毛片 | 成人18视频在线观看 | 黄网在线免费看 | 500短篇超污多肉推荐短视频 | 成人a毛片免费观看网站 | 亚洲黄色一级 | 欧美视频精品在线 | 欧美激情 亚洲 | 亚洲狼人香蕉香蕉在线28 | 黄色毛片在线看 | 亚洲欧美色视频 | 激情久久久久久久久久 |