Hogyan lehet megállítani a végrehajtást egy bizonyos idő után a Java-ban

1. Áttekintés

Ebben a cikkben megtudhatjuk, hogyan fejezhetjük be a régóta futó végrehajtást egy bizonyos idő után. Megvizsgáljuk a probléma különféle megoldásait. Ezenkívül kitérünk néhány buktatójukra.

2. Hurok használata

Képzelje el, hogy egy csomó elemet ciklusban dolgozunk fel, például egy e-kereskedelmi alkalmazásban a termék egyes részleteit, de lehet, hogy nem szükséges az összes elemet kitölteni.

Valójában csak egy bizonyos ideig szeretnénk feldolgozni, ezt követően pedig le akarjuk állítani a végrehajtást, és meg akarjuk mutatni, amit a lista addig feldolgozott.

Lássunk egy gyors példát:

hosszú indítás = System.currentTimeMillis (); hosszú vég = kezdet + 30 * 1000; while (System.currentTimeMillis () <end) {// Néhány drága művelet az elemen. }

Itt a hurok megszakad, ha az idő meghaladta a 30 másodperces határt. Van néhány figyelemre méltó pont a fenti megoldásban:

  • Alacsony pontosság: A hurok hosszabb ideig futhat, mint az előírt határidő. Ez az egyes iterációk időtartamától függ. Például, ha minden iteráció akár 7 másodpercet is igénybe vehet, akkor a teljes idő akár 35 másodpercig is eltarthat, ami körülbelül 17% -kal hosszabb, mint a kívánt 30 másodperces időhatár
  • Letiltás: Az ilyen feldolgozás a fő szálban nem biztos, hogy jó ötlet, mivel sokáig blokkolja. Ehelyett ezeket a műveleteket le kell választani a fő szálról

A következő részben megvitatjuk, hogy a megszakítás alapú megközelítés hogyan szünteti meg ezeket a korlátozásokat.

3. Megszakító mechanizmus használata

Itt külön szálat fogunk használni a régóta futó műveletek végrehajtásához. A fő szál megszakítási jelet küld a munkásszálnak az időtúllépéskor.

Ha a munkásszál még él, akkor elkapja a jelet és leállítja a végrehajtását. Ha a munkavállaló az időkorlát előtt befejezi, az nem lesz hatással a munkavállalói szálra.

Vessünk egy pillantást a munkás szálra:

class LongRunningTask implementálja a Runnable {@Override public void run () {try {while {! (Thread.interrupted ()) {Thread.sleep (500); }} catch (InterruptedException e) {// naplóhiba}}}

Itt, Szál.alszik hosszú ideje működő műveletet szimulál. Ehelyett bármilyen más művelet is történhet. Fontos ellenőrizze a megszakítás jelzőt, mert nem minden művelet megszakítható. Tehát ezekben az esetekben manuálisan ellenőriznünk kell a zászlót.

Ezenkívül minden iterációban ellenőriznünk kell ezt a jelzőt, hogy a szál legfeljebb egy iteráció késleltetése alatt leálljon.

Ezután a megszakítási jel küldésének három különböző mechanizmusát tárgyaljuk.

3.1. Használva Időzítő

Alternatív megoldásként létrehozhatunk egy TimerTask megszakítani a munkásszálat időtúllépéskor:

osztály A TimeOutTask kiterjeszti a TimerTask {private Thread t; privát időzítő; TimeOutTask (Thread t, Timer timer) {this.t = t; this.timer = időzítő; } public void run () {if (t! = null && t.isAlive ()) {t.megszakít (); időzítő.törlés (); }}}

Itt definiáltuk a TimerTask amely létrehozása során munkásszálat vesz igénybe. Majd szakítsa meg a munkásszálat annak meghívására fuss módszer. A Időzítő kiváltja a TimerTask a megadott késedelem után:

Téma t = új Téma (új LongRunningTask ()); Időzítő időzítő = új időzítő (); timer.schedule (új TimeOutTask (t, időzítő), 30 * 1000); t.start ();

3.2. A módszer használata Jövő # kap

Használhatjuk a kap módszer a Jövő ahelyett, hogy a Időzítő:

ExecutorService végrehajtó = Executors.newSingleThreadExecutor (); Future future = végrehajtó.submit (új LongRunningTask ()); próbáld ki a {f.get (30, TimeUnit.SECONDS); } catch (TimeoutException e) {f.cancel (true); } végül {service.shutdownNow (); }

Itt használtuk a ExecutorService beküldeni a munkaszálat, amely visszaadja a Jövő, akinek kap metódus a megadott időig blokkolja a főszálat. Fel fog emelni egy TimeoutException a megadott időtúllépés után. Ban,-ben fogás blokkot, megszakítjuk a munkásszálat a megszünteti módszer a Future tárgy.

E megközelítés legfőbb előnye az előzőhöz képest az, hogy egy készletet használ a szál kezelésére, míg a Időzítő csak egyetlen szálat használ (nincs készlet).

3.3. Használva ScheduledExcecutorSercvice

Használhatjuk is ScheduledExecutorService hogy megszakítsam a feladatot. Ez az osztály az an kiterjesztése ExecutorService és ugyanazt a funkcionalitást biztosítja a végrehajtás ütemezésével foglalkozó számos módszer hozzáadásával. Ez a megadott időegységek bizonyos késleltetése után hajthatja végre az adott feladatot:

ScheduledExecutorService végrehajtó = Executors.newScheduledThreadPool (2); Future future = végrehajtó.submit (új LongRunningTask ()); végrehajtó.schedule (új Runnable () {public void run () {future.cancel (true);}}, 1000, TimeUnit.MILLISECONDS); végrehajtó.leállítás ();

Itt létrehoztunk egy ütemezett kettes méretű szálkészletet a módszerrel newScheduledThreadPool. A ScheduledExecutorService #menetrend módszer a Futható, egy késleltetési érték és a késés mértékegysége.

A fenti program ütemezi a feladat végrehajtását a beküldés időpontjától számított egy másodperc elteltével. Ez a feladat törli az eredeti régóta futó feladatot.

Ne feledje, hogy az előző megközelítéstől eltérően nem blokkoljuk a fő szálat a Jövő # kap módszer. Ebből kifolyólag, ez a legelőnyösebb megközelítés az összes fent említett megközelítés között.

4. Van-e garancia?

Nincs garancia arra, hogy a végrehajtás egy bizonyos idő után leáll. A fő ok az, hogy nem minden blokkolási módszer megszakítható. Valójában csak néhány jól definiálható, megszakítható módszer létezik. Így, ha egy szál megszakad és beállít egy zászlót, semmi más nem fog történni, amíg el nem éri ezen megszakítható módszerek egyikét.

Például, olvas és ír a módszerek csak akkor szakíthatók meg, ha egy Megszakítható csatorna. BufferedReader nem egy Megszakítható csatorna. Tehát, ha a szál egy fájl olvasására használja, hívja megszakítás () ezen a szálon blokkolta a olvas módszer nincs hatása.

Mindazonáltal kifejezetten ellenőrizhetjük a megszakítás jelzőt minden ciklusban történő olvasás után. Ez ésszerű garanciát jelent a szál némi késéssel történő leállítására. De ez nem garantálja a szál szigorú időn belüli leállítását, mert nem tudjuk, hogy egy olvasási művelet mennyi időt vehet igénybe.

Másrészt a várjon módszere Tárgy osztály megszakítható. Így a szál elakadt a várjon módszer azonnal dob egy InterruptedException miután a megszakítás jelző be van állítva.

A blokkolási módszereket azonosíthatjuk a dobInterruptedException módszerük aláírásaiban.

Egy fontos tanács: kerülje az elavult használatát Thread.stop () módszer. A szál leállítása feloldja az összes reteszelt monitort. Ez azért történik, mert ThreadDeath kivétel, amely a veremben terjed.

Ha a monitorok által korábban védett objektumok bármelyike ​​inkonzisztens állapotban volt, akkor az inkonzisztens objektumok más szálak számára láthatóvá válnak. Ez önkényes magatartáshoz vezethet, amelyet nagyon nehéz felismerni és aminek oka van.

5. Következtetés

Ebben az oktatóanyagban megtanultunk különféle technikákat a végrehajtás leállításához egy adott idő után, az egyes előnyökkel és hátrányokkal együtt. A teljes forráskód megtalálható a GitHub oldalon.