Ako zastaviť vykonávanie po určitom čase v prostredí Java

1. Prehľad

V tomto článku sa dozvieme, ako môžeme po určitom čase ukončiť dlhotrvajúce vykonávanie. Preskúmame rôzne riešenia tohto problému. Pokryjeme tiež niektoré z ich nástrah.

2. Pomocou slučky

Predstavte si, že spracovávame veľa položiek v cykle, napríklad niektoré podrobnosti o položkách produktu v aplikácii elektronického obchodu, ale nemusí byť potrebné dokončiť všetky položky.

V skutočnosti by sme to chceli spracovať iba do určitého času a potom chceme zastaviť vykonávanie a ukázať, čo všetko zoznam do tej doby spracoval.

Pozrime sa na krátky príklad:

dlhý štart = System.currentTimeMillis (); dlhý koniec = štart + 30 * 1000; while (System.currentTimeMillis () <end) {// Niektoré nákladné operácie s položkou. }

Tu sa slučka preruší, ak čas prekročil hranicu 30 sekúnd. Vo vyššie uvedenom riešení sú niektoré pozoruhodné body:

  • Nízka presnosť: Smyčka môže trvať dlhšie ako stanovený časový limit. To bude závisieť od času, ktorý môže trvať každá iterácia. Ak napríklad každá iterácia môže trvať až 7 sekúnd, celkový čas môže dosiahnuť až 35 sekúnd, čo je o zhruba 17% dlhšie ako požadovaný časový limit 30 sekúnd.
  • Blokovanie: Takéto spracovanie v hlavnom vlákne nemusí byť dobrý nápad, pretože ho na dlho zablokuje. Namiesto toho by sa tieto operácie mali oddeliť od hlavného vlákna

V nasledujúcej časti si povieme, ako prístup založený na prerušení eliminuje tieto obmedzenia.

3. Používanie mechanizmu prerušenia

Tu použijeme samostatné vlákno na vykonanie dlhotrvajúcich operácií. Hlavné vlákno pošle signál prerušenia do pracovného vlákna po uplynutí časového limitu.

Ak je pracovné vlákno stále nažive, zachytí signál a zastaví jeho vykonávanie. Ak pracovník skončí pred vypršaním časového limitu, nebude to mať žiadny vplyv na pracovné vlákno.

Pozrime sa na pracovné vlákno:

trieda LongRunningTask implementuje Runnable {@Override public void run () {try {while (! Thread.interrupted ()) {Thread.sleep (500); }} catch (InterruptedException e) {// chyba protokolu}}}

Tu, Závit. Spánok simuluje dlhotrvajúcu operáciu. Namiesto toho by mohla byť vykonaná akákoľvek iná operácia. Je to dôležité skontrolujte príznak prerušenia, pretože nie všetky operácie sú prerušiteľné. V týchto prípadoch by sme teda mali vlajku skontrolovať ručne.

Mali by sme tiež skontrolovať tento príznak pri každej iterácii, aby sme sa ubezpečili, že sa vlákno prestane vykonávať samé maximálne v oneskorení jednej iterácie.

Ďalej sa budeme venovať trom rôznym mechanizmom vysielania signálu prerušenia.

3.1. Pomocou a Časovač

Prípadne môžeme vytvoriť a TimerTask prerušiť pracovné vlákno po uplynutí časového limitu:

trieda TimeOutTask rozširuje TimerTask {private Thread t; súkromný časovač; TimeOutTask (vlákno t, časovač časovača) {this.t = t; this.timer = časovač; } public void run () {if (t! = null && t.isAlive ()) {t.interrupt (); timer.cancel (); }}}

Tu sme definovali a TimerTask ktorá vezme pracovné vlákno v čase svojho vytvorenia. Bude to preruší pracovné vlákno po jeho vyvolaní bežať metóda. The Časovač spustí TimerTask po stanovenom oneskorení:

Vlákno t = nové vlákno (nový LongRunningTask ()); Časovač časovača = nový Časovač (); timer.schedule (new TimeOutTask (t, timer), 30 * 1000); t.start ();

3.2. Pomocou metódy Budúce # dostať

Môžeme tiež použiť dostať metóda a Budúcnosť namiesto použitia a Časovač:

ExecutorService vykonávateľ = Executors.newSingleThreadExecutor (); Budúca budúcnosť = executor.submit (nový LongRunningTask ()); try {f.get (30, TimeUnit.SECONDS); } catch (TimeoutException e) {f.cancel (true); } nakoniec {service.shutdownNow (); }

Tu sme použili ExecutorService na odoslanie pracovného vlákna, ktoré vracia inštanciu Budúcnosť, ktorého dostať metóda zablokuje hlavné vlákno do zadaného času. Zdvihne a Výnimka časového limitu po zadanom časovom limite. V chytiť blok, prerušujeme pracovné vlákno volaním Zrušiť metóda na Fčistý objekt.

Hlavnou výhodou tohto prístupu oproti predchádzajúcemu je to, že je používa fond na správu vlákna, zatiaľ čo Časovač používa iba jedno vlákno (bez fondu).

3.3. Pomocou a ScheduledExcecutorSercvice

Môžeme tiež použiť ScheduledExecutorService prerušiť úlohu. Táto trieda je rozšírením ExecutorService a poskytuje rovnakú funkcionalitu pridaním niekoľkých metód, ktoré sa zaoberajú plánovaním vykonávania. To môže danú úlohu vykonať po určitom oneskorení nastavených časových jednotiek:

ScheduledExecutorService vykonávateľ = Executors.newScheduledThreadPool (2); Budúca budúcnosť = executor.submit (nový LongRunningTask ()); executor.schedule (new Runnable () {public void run () {future.cancel (true);}}, 1000, TimeUnit.MILLISECONDS); executor.shutdown ();

Tu sme pomocou metódy vytvorili naplánovaný fond vlákien veľkosti dva newScheduledThreadPool. The ScheduledExecutorService #harmonogram metóda vyžaduje a Spustiteľné, hodnota oneskorenia a jednotka oneskorenia.

Vyššie uvedený program naplánuje vykonanie úlohy na jednu sekundu od času odoslania. Táto úloha zruší pôvodnú dlhodobú úlohu.

Upozorňujeme, že na rozdiel od predchádzajúceho prístupu neblokujeme hlavné vlákno volaním znaku Budúce # dostať metóda. Preto je to najviac preferovaný prístup spomedzi všetkých vyššie spomenutých prístupov.

4. Existuje záruka?

Nie je možné zaručiť, že sa vykonávanie po určitom čase zastaví. Hlavným dôvodom je, že nie všetky metódy blokovania sú prerušiteľné. V skutočnosti existuje iba niekoľko dobre definovaných metód, ktoré sú prerušiteľné. Takže ak je vlákno prerušené a je nastavený príznak, nič iné sa nestane, kým nedosiahne jednu z týchto prerušiteľných metód.

Napríklad, čítať a napíš metódy sú prerušiteľné, iba ak sú vyvolané v prúdoch vytvorených pomocou Prerušiteľný kanál. BufferedReader nie je Prerušiteľný kanál. Pokiaľ ho teda vlákno používa na čítanie súboru, volá prerušiť() na tomto vlákne blokované v čítať metóda nemá žiadny vplyv.

Môžeme však výslovne skontrolovať príznak prerušenia po každom prečítaní v slučke. To dá primeranú záruku na zastavenie vlákna s určitým oneskorením. To však nezaručuje zastavenie vlákna po prísnom čase, pretože nevieme, koľko času môže trvať operácia čítania.

Na druhej strane počkaj metóda Objekt trieda je prerušiteľná. Teda vlákno blokované v počkaj metóda okamžite hodí Prerušená výnimka po nastavení príznaku prerušenia.

Metódy blokovania môžeme identifikovať vyhľadaním a hodíPrerušená výnimka v ich podpisoch metód.

Jedna dôležitá rada je nepoužívajte zastarané Thread.stop () metóda. Zastavenie vlákna spôsobí, že odomkne všetky uzamknuté monitory. To sa deje z dôvodu ThreadDeath výnimka, ktorá sa šíri hore zásobníkom.

Ak bol niektorý z objektov predtým chránených týmito monitormi v nekonzistentnom stave, nekonzistentné objekty sa stanú viditeľnými pre ďalšie vlákna. To môže viesť k svojvoľnému správaniu, ktoré je veľmi ťažké odhaliť a odôvodniť.

5. Záver

V tomto tutoriáli sme sa naučili rôzne techniky zastavenia vykonávania po danom čase spolu s výhodami a nevýhodami každého z nich. Celý zdrojový kód nájdete na GitHub.


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