大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

專注Java教育14年 全國咨詢/投訴熱線:400-8080-105
動力節點LOGO圖
始于2009,口口相傳的Java黃埔軍校
首頁 hot資訊 Spring定時器詳解

Spring定時器詳解

更新時間:2021-12-10 10:10:58 來源:動力節點 瀏覽3722次

Spring定時器是什么?小編來告訴大家。

1. 定時器——基礎

Timer和TimerTask是用于在后臺線程中調度任務的 java util 類。簡而言之 - TimerTask是要執行的任務,Timer是調度程序。

2. 安排一次任務

(1)在給定的延遲之后

讓我們首先在Timer的幫助下簡單地運行單個任務:

@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);
}

現在,這在一定的延遲后執行任務,作為schedule()方法的第二個參數給出 。我們將在下一節看到如何在給定的日期和時間安排任務。

請注意,如果我們正在運行這是一個 JUnit 測試,我們應該添加一個Thread.sleep(delay * 2)調用,以允許 Timer 的線程在 Junit 測試停止執行之前運行任務。

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

現在,讓我們看看Timer#schedule(TimerTask, Date)方法,它接受一個Date而不是long作為它的第二個參數,允許我們在某個時刻安排任務,而不是在延遲之后。

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

我們可以創建一個DatabaseMigrationTask類來處理該遷移:

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);
    }
}

為簡單起見,我們代表著兩個數據庫通過列表的 字符串。簡單地說,我們的遷移包括將第一個列表中的數據放入第二個列表中。

要在所需的時刻執行此遷移,我們必須使用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);

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

然后,在twoSecondsLater指示的時間執行遷移:

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

雖然我們在這一刻之前,遷移不會發生。

3. 安排一個可重復的任務

既然我們已經介紹了如何安排任務的單次執行,讓我們看看如何處理可重復的任務。

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

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

假設我們想每兩秒安排一次任務,第一次執行需要一秒鐘,第二次執行需要兩秒鐘但延遲一秒鐘。然后,第三次執行將在第五秒開始:

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

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

讓我們重用前面的例子,在固定速率下,第二個任務將在三秒后開始(因為延遲)。但是,四秒后的第三個(尊重每兩秒執行一次的初始計劃):

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

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

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

為什么有兩個重載?因為仍然有可能在某個時刻或在某個延遲之后開始任務。

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

還值得一提的是,如果任務花費的時間比執行周期長,無論我們使用固定延遲還是固定速率,它都會延遲整個執行鏈。

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

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

為什么有兩個重載?因為仍然有可能在某個時刻或在某個延遲之后開始任務。

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

還值得一提的是,如果任務花費的時間比執行周期長,無論我們使用固定延遲還是固定速率,它都會延遲整個執行鏈。

(1)固定延遲

現在,讓我們假設我們想要實施一個時事通訊系統,每周向我們的關注者發送一封電子郵件。在這種情況下,重復性任務似乎很理想。

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

讓我們首先設計一個 NewsletterTask:

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

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

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

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

當然,我們只對少數情況進行測試:

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

正如我們所看到的,每次執行之間至少有 1 秒的時間,但它們有時會延遲一毫秒。這種現象是由于我們決定使用固定延遲重復。

(2)固定利率

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

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

這一次,執行不會被前面的延遲:

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)安排每日任務

接下來,讓我們每天運行一次任務:

@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.取消定時器和TimerTask

可以通過以下幾種方式取消任務的執行:

(1)在運行中取消TimerTask

通過在TimerTask本身的run()方法實現中調用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)取消定時器

通過在Timer對象上調用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)停止內部運行的TimerTask的線程

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

@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實現中的 TODO 指令——為了運行這個簡單的例子,我們需要實際停止線程。

在現實世界的自定義線程實現中,應該支持停止線程,但在這種情況下,我們可以忽略棄用并在 Thread 類本身上使用簡單的停止API。

5.定時器vs ExecutorService

你也可以很好地利用一個ExecutorService來調度定時器任務,而不是使用定時器。

以下是如何以指定的時間間隔運行重復任務的快速示例:

@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解決方案的主要區別是什么:

定時器可以對系統時鐘的變化敏感;ScheduledThreadPoolExecutor不是

定時器只有一個執行線程;ScheduledThreadPoolExecutor可以配置任意數量的線程

TimerTask 中拋出的運行時異常會殺死線程,因此后續的計劃任務不會繼續運行;with ScheduledThreadExecutor - 當前任務將被取消,但其余的將繼續運行

6.結論

本教程說明了可以利用Java 中內置的簡單而靈活的Timer和TimerTask基礎結構來快速安排任務的多種方法。當然,如果您需要,Java 世界中還有更復雜和完整的解決方案——例如Quartz 庫——但這是一個非常好的起點。

這些示例的實現可以在GitHub項目中找到——這是一個基于 Eclipse 的項目,因此應該很容易導入和運行。

提交申請后,顧問老師會電話與您溝通安排學習

免費課程推薦 >>
技術文檔推薦 >>
主站蜘蛛池模板: 99精品国产兔费观看66 | 99国产成人精品2021 | 日本1区二区三区公司 | 在线你懂得 | 日韩欧美亚洲每的更新在线 | 99国产精品热久久久久久夜夜嗨 | 亚洲精品欧美精品日韩精品 | 男人天堂成人 | 激情婷婷在线 | 欧美亚洲国产精品久久高清 | 亚洲欧美在线综合一区二区三区 | 青春禁区视频在线观看动漫版 | 天海翼一区 在线播放 | 草莓视频在线观看精品最新 | 日日夜人人澡人人澡人人看免 | 亚洲国产99在线精品一区二区 | 色综合久久88色综合天天 | 最新国产福利在线 | 日韩美女视频一区 | 91啪国自产在线高清观看 | 久热这里只有精 | 青青青青青国产免费观看 | 亚洲精品美女久久久久网站 | 黄色成人在线网站 | 日本在线视频不卡 | 中文亚洲欧美 | 一级理论片免费观看在线 | 久久综合久美利坚合众国 | 国产精品合集一区二区 | 狠狠干狠 | 免费观看a黄一级视频 | 伊人久久丁香色婷婷啪啪 | 98色花堂国产精品首页 | 久久无码精品一区二区三区 | 免费欧美一级片 | 国产日韩不卡免费精品视频 | 99精品免费在线观看 | 有色视频在线观看免费高清 | 国产香蕉视频在线 | 欧美一区二区免费 | 五月婷婷伊人 |