Java Timer

1. Időzítő - az alapok

Időzítő és TimerTask a java util osztályok a feladatok háttérszálon történő ütemezésére szolgálnak. Pár szóban - TimerTask a végrehajtandó feladat és Időzítő az ütemező.

2. Ütemezzen be egy feladatot egyszer

2.1. Adott késés után

Kezdjük egyszerűen egyetlen feladat futtatása a segítségével Időzítő:

@Test public void givenUsingTimer_whenSchedulingTaskOnce_thenCorrect () {TimerTask task = new TimerTask () {public void run () {System.out.println ("Feladat végrehajtva:" + új dátum () + "n" + "A szál neve:" + Thread.currentThread (). GetName ()); }}; Időzítő időzítő = új időzítő ("Időzítő"); hosszú késés = 1000L; timer.schedule (feladat, késés); }

Most, ez bizonyos késedelem után elvégzi a feladatot, amely a menetrend() módszer. A következő szakaszban láthatjuk, hogyan ütemezhetünk egy feladatot egy adott dátumra és időpontra.

Vegye figyelembe, hogy ha futunk, ez egy JUnit-teszt, akkor hozzá kell adnunk a-t Thread.sleep (késés * 2) hívás, hogy az időzítő szála futtassa a feladatot, mielőtt a Junit teszt leállítaná a végrehajtását.

2.2. Adott időpontban és időpontban

Most nézzük meg a Időzítő # ütemezése (TimerTask, Dátum) módszer, amely a Dátum a helyett hosszú második paramétere, amely lehetővé teszi számunkra, hogy a feladatot egy bizonyos pillanatban ütemezzük, nem pedig késedelem után.

Ezúttal képzeljük el, hogy van egy régi, régi adatbázisunk, és annak adatait egy jobb sémával rendelkező új adatbázisba szeretnénk migrálni.

Hozhatnánk létre a DatabaseMigrationTask osztály, amely az átállást kezeli:

public class DatabaseMigrationTask kiterjeszti a TimerTask {private List oldDatabase; privát List newDatabase; public DatabaseMigrationTask (List oldDatabase, List newDatabase) {this.oldDatabase = oldDatabase; this.newDatabase = newDatabase; } @Orride public void run () {newDatabase.addAll (oldDatabase); }}

Az egyszerűség kedvéért a két adatbázist a-val ábrázoljuk Lista nak,-nek Húr. Egyszerűen fogalmazva: a migrációnk abból áll, hogy az első lista adatait a másodikba helyezzük.

A migráció kívánt pillanatban történő végrehajtásához a túlterhelt verziót kell használnunk menetrend() módszer:

List oldDatabase = Arrays.asList ("Harrison Ford", "Carrie Fisher", "Mark Hamill"); List newDatabase = new ArrayList (); LocalDateTime twoSecondsLater = LocalDateTime.now (). PlusSeconds (2); Date twoSecondsLaterAsDate = Date.from (twoSecondsLater.atZone (ZoneId.systemDefault ()). ToInstant ()); új időzítő (). ütemezés (új DatabaseMigrationTask (oldDatabase, newDatabase), twoSecondsLaterAsDate);

Mint láthatjuk, az áttelepítési feladatot, valamint a végrehajtás dátumát megadjuk a menetrend() módszer.

Ezután az áttelepítést a twoSecondsLater:

míg a (LocalDateTime.now (). isBefore (twoSecondsLater)) {assertThat (newDatabase) .isEmpty (); Szál.alszik (500); } assertThat (newDatabase) .containsExactlyElementsOf (oldDatabase);

Amíg ezen pillanat előtt vagyunk, a migráció nem következik be.

3. Ismételhető feladat ütemezése

Most, hogy áttekintettük a feladat egyszeri végrehajtásának ütemezését, nézzük meg, hogyan kell kezelni az ismételhető feladatokat.

Ismételten számos lehetőséget kínál a Időzítő osztály: Beállíthatjuk az ismétlést akár fix késleltetés, akár fix sebesség megfigyelésére.

A fix késleltetés azt jelenti, hogy a végrehajtás a legutóbbi végrehajtás kezdete után egy bizonyos ideig kezdődik, még akkor is, ha késik (ezért maga késik).

Tegyük fel, hogy két másodpercenként be akarunk ütemezni néhány feladatot, és hogy az első végrehajtás egy másodpercet vesz igénybe, a második pedig kettőt, de egy másodperccel késik. Ezután a harmadik végrehajtás az ötödik másodpercnél kezdődne:

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

Másrészről, a fix kamatláb azt jelenti, hogy minden végrehajtás betartja a kezdeti ütemtervet, függetlenül attól, hogy egy korábbi végrehajtás késik-e.

Használjuk újra előző példánkat, rögzített sebességgel, a második feladat három másodperc múlva indul (a késés miatt). De a harmadik négy másodperc után (tiszteletben tartva a két másodpercenként egy végrehajtás kezdeti ütemezését):

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

Ezt a két alapelvet lefedve megnézhetjük, hogyan kell használni őket.

A rögzített késleltetésű ütemezés használatához további két túlterhelés van menetrend() módszerrel, mindegyik felvesz egy extra paramétert, amely milliszekundumokban adja meg a periodicitást.

Miért két túlterhelés? Mivel továbbra is fennáll a lehetőség egy adott pillanatban vagy bizonyos késedelem után elindítani a feladatot.

Ami a fix díjtételű ütemezést illeti, megvan a kettő scheduleAtFixedRate () a módszerek ezredmásodpercekben is periodicitást vesznek fel. Ismét van egy módszerünk a feladat elindítására egy adott dátumon és időpontban, és egy másik módszer, amellyel egy adott késés után elindíthatjuk.

Érdemes megemlíteni azt is, hogy ha egy feladat végrehajtása több időt vesz igénybe, mint az adott időszak, akkor késlelteti a teljes végrehajtási láncot, függetlenül attól, hogy fix késleltetésű vagy fix sebességű-e.

3.1. Fix késéssel

Képzeljük el, hogy hírleveles rendszert szeretnénk bevezetni, és minden héten e-mailt küldünk követőinknek. Ebben az esetben egy ismétlődő feladat ideálisnak tűnik.

Szóval ütemezzük másodpercenként a hírlevelet, amely alapvetően spam jellegű, de mivel a küldés hamis, készek vagyunk!

Tervezzük először a NewsletterTask:

public class NewsletterTask kiterjeszti a TimerTask {@Orride public void run () {System.out.println ("E-mail elküldve:" + LocalDateTime.ofInstant (Instant.ofEpochMilli (tervezettExecutionTime ()), ZoneId.systemDefault ())); }}

A feladat minden végrehajtásakor kinyomtatja az ütemezett idejét, amelyet a TimerTask # tervezettExecutionTime () módszer.

Akkor mi van, ha ezt a feladatot másodpercenként ütemezni akarjuk fix késleltetésű módban? A túlterhelt verziót kell használnunk menetrend() korábban beszéltünk:

új időzítő (). ütemezés (új NewsletterTask (), 0, 1000); for (int i = 0; i <3; i ++) {Szál.alszik (1000); }

Természetesen csak néhány esetre végezzük a teszteket:

E-mail elküldve: 2020-01-01T10: 50: 30.860 E-mail elküldve: 2020-01-01T10: 50: 31.860 E-mail elküldve: 2020-01-01T10: 50: 32.861 : 33,861

Mint láthatjuk, az egyes végrehajtások között van legalább egy másodperc, de ezeket néha milliszekundummal késik. Ez a jelenség annak köszönhető, hogy elhatároztuk a fix késleltetésű ismétlést.

3.2. Fix árfolyammal

És mi lenne, ha fix arányú ismétlést alkalmaznánk? Akkor használnunk kellene a ütemezettAtFixedRate () módszer:

új időzítő (). scheduleAtFixedRate (új NewsletterTask (), 0, 1000); for (int i = 0; i <3; i ++) {Szál.alszik (1000); }

Ezúttal, a kivégzéseket az előzőek nem késik:

E-mail elküldve: 2020-01-01T10: 55: 03.805 E-mail elküldve: 2020-01-01T10: 55: 04.805 E-mail elküldve: 2020-01-01T10: 55: 05.805 : 06.805

3.3. Ütemezzen napi feladatot

Ezután menjünk futtasson egy feladatot naponta egyszer:

@Test public void givenUsingTimer_whenSchedulingDailyTask_thenCorrect () {TimerTask repeatTask = new TimerTask () {public void run () {System.out.println ("Feladat végrehajtva" + új dátum ()); }}; Időzítő időzítő = új időzítő ("Időzítő"); hosszú késés = 1000L; hosszú periódus = 1000L * 60L * 60L * 24L; timer.scheduleAtFixedRate (repeatTask, delay, period); }

4. Mégse Időzítő és TimerTask

Egy feladat végrehajtása néhány módon törölhető:

4.1. Törölje a TimerTask Belül Fuss

A TimerTask.cancel () módszer a fuss() módszer megvalósítása a TimerTask maga:

@Test public void givenUsingTimer_whenCancelingTimerTask_thenCorrect () dobja az InterruptedException {TimerTask task = new TimerTask () {public void run () {System.out.println ("Feladat végrehajtva" + új dátum ()); megszünteti(); }}; Időzítő időzítő = új időzítő ("Időzítő"); timer.scheduleAtFixedRate (feladat, 1000L, 1000L); Szál.alszik (1000L * 2); }

4.2. Törölje a Időzítő

Azzal, hogy felhívta a Timer.cancel () módszer a Időzítő tárgy:

@Test public void givenUsingTimer_whenCancelingTimer_thenCorrect () dobja az InterruptedException {TimerTask task = new TimerTask () {public void run () {System.out.println ("Feladat végrehajtva" + új dátum ()); }}; Időzítő időzítő = új időzítő ("Időzítő"); timer.scheduleAtFixedRate (feladat, 1000L, 1000L); Szál.alszik (1000L * 2); időzítő.törlés (); }

4.3. Állítsa le a szálat TimerTask Belül Fuss

Megállíthatja a szálat is a fuss módszerrel, így törölve a teljes feladatot:

@Test public void givenUsingTimer_whenStoppingThread_thenTimerTaskIsCancelled () thrumps InterruptedException {TimerTask task = new TimerTask () {public void run () {System.out.println ("Feladat végrehajtva" + új dátum ()); // TODO: itt állítsa le a szálat}}; Időzítő időzítő = új időzítő ("Időzítő"); timer.scheduleAtFixedRate (feladat, 1000L, 1000L); Szál.alszik (1000L * 2); }

Figyelje meg a TODO utasítást a fuss megvalósítás - ennek az egyszerű példának a futtatásához le kell állítanunk a szálat.

A valós egyéni szál megvalósításában támogatni kell a szál leállítását, de ebben az esetben figyelmen kívül hagyhatjuk a megszüntetést, és használhatjuk az egyszerű álljon meg API-t magában a Thread osztályban.

5. Időzítő vs. ExecutorService

Az időzítő használata helyett az ExecutorService-t is jól ki lehet használni az időzítő feladatok ütemezéséhez.

Íme egy gyors példa arra, hogyan lehet egy ismételt feladatot meghatározott időközönként futtatni:

@Test public void givenUsingExecutorService_whenSchedulingRepeatedTask_thenCorrect () thrugs InterruptedException {TimerTask repeatTask = new TimerTask () {public void run () {System.out.println ("Feladat végrehajtva" + új dátum ()); }}; ScheduledExecutorService végrehajtó = Executors.newSingleThreadScheduledExecutor (); hosszú késés = 1000L; hosszú periódus = 1000L; végrehajtó.scheduleAtFixedRate (ismételtTask, késleltetés, periódus, TimeUnit.MILLISECONDS); Szál.alszik (késleltetés + periódus * 3); végrehajtó.leállítás (); }

Tehát mi a fő különbség a Időzítő és a ExecutorService megoldás:

  • Időzítő érzékeny lehet a rendszer órájának változásaira; ScheduledThreadPoolExecutor nem
  • Időzítő csak egy végrehajtási szál van; ScheduledThreadPoolExecutor tetszőleges számú szálral konfigurálható
  • A futásidejű kivételek a TimerTask ölje meg a szálat, így az ütemezett feladatok követése nem fut tovább; val vel ScheduledThreadExecutor - az aktuális feladat törlődik, de a többi továbbra is fut

6. Következtetés

Ez az oktatóanyag szemlélteti az egyszerű, ugyanakkor rugalmas használat számos módját Időzítő és TimerTask Java-ba épített infrastruktúra a feladatok gyors ütemezéséhez. Természetesen vannak sokkal összetettebb és teljesebb megoldások a Java világban, ha szükség van rájuk - például a Quartz könyvtár -, de ez nagyon jó hely a kezdéshez.

Ezeknek a példáknak a megvalósítása megtalálható a GitHub projektben - ez egy Eclipse-alapú projekt, ezért könnyen importálhatónak és futtathatónak kell lennie.


$config[zx-auto] not found$config[zx-overlay] not found