Hvordan stoppe utførelsen etter en viss tid i Java

1. Oversikt

I denne artikkelen lærer vi hvordan vi kan avslutte en langvarig kjøring etter en viss tid. Vi vil utforske de forskjellige løsningene på dette problemet. Vi vil også dekke noen av fallgruvene deres.

2. Bruke en løkke

Tenk deg at vi behandler en haug med varer i en løkke, for eksempel noen detaljer om produktvarene i en e-handelsapplikasjon, men at det kanskje ikke er nødvendig å fullføre alle varene.

Faktisk vil vi bare behandle opptil et bestemt tidspunkt, og etter det vil vi stoppe utførelsen og vise hva listen har behandlet frem til den tiden.

La oss se et raskt eksempel:

lang start = System.currentTimeMillis (); lang slutt = start + 30 * 1000; while (System.currentTimeMillis () <end) {// Noe kostbar handling på varen. }

Her vil sløyfen bryte hvis tiden har overskredet grensen på 30 sekunder. Det er noen bemerkelsesverdige punkter i løsningen ovenfor:

  • Lav nøyaktighet: Sløyfen kan kjøre lenger enn den pålagte tidsgrensen. Dette vil avhenge av tiden hver iterasjon kan ta. For eksempel, hvis hver iterasjon kan ta opptil 7 sekunder, kan den totale tiden gå opp til 35 sekunder, som er rundt 17% lenger enn ønsket tidsgrense på 30 sekunder.
  • Blokkering: Slik behandling i hovedtråden er kanskje ikke en god ide, da den vil blokkere den i lang tid. I stedet bør disse operasjonene kobles fra hovedtråden

I neste avsnitt vil vi diskutere hvordan den avbruddsbaserte tilnærmingen eliminerer disse begrensningene.

3. Bruke en avbruddsmekanisme

Her bruker vi en egen tråd for å utføre de langvarige operasjonene. Hovedtråden vil sende et avbruddssignal til arbeidertråden ved tidsavbrudd.

Hvis arbeidertråden fremdeles er i live, vil den fange signalet og stoppe utførelsen. Hvis arbeidstakeren er ferdig før tidsavbruddet, vil det ikke ha noen innvirkning på arbeidertråden.

La oss ta en titt på arbeidertråden:

klasse LongRunningTask implementerer Runnable {@Override public void run () {try {while (! Thread.interrupted ()) {Thread.sleep (500); }} fangst (InterruptedException e) {// loggfeil}}}

Her, Tråd. Sove simulerer en langvarig operasjon. I stedet for dette kan det være noen annen operasjon. Det er viktig å sjekk avbruddsflagget fordi ikke alle operasjonene er avbrytbare. Så i slike tilfeller bør vi manuelt sjekke flagget.

Vi bør også sjekke dette flagget i hver iterasjon for å sikre at tråden slutter å utføre seg selv innen forsinkelsen av en iterasjon på det meste.

Deretter dekker vi tre forskjellige mekanismer for sending av avbruddssignalet.

3.1. Bruker en Timer

Alternativt kan vi lage en TimerTask å avbryte arbeidertråden ved timeout:

klasse TimeOutTask utvider TimerTask {privat tråd t; privat timer; TimeOutTask (tråd t, tidtaker) {this.t = t; this.timer = timer; } public void run () {if (t! = null && t.isAlive ()) {t.interrupt (); timer.cancel (); }}}

Her har vi definert en TimerTask som tar en arbeidstråd på tidspunktet for opprettelsen. Det vil det avbryte arbeidertråden ved påkalling av dens løpe metode. De Timer vil utløse TimerTask etter den angitte forsinkelsen:

Tråd t = ny tråd (ny LongRunningTask ()); Timer timer = new Timer (); timer.schedule (ny TimeOutTask (t, timer), 30 * 1000); t.start ();

3.2. Bruke metoden Fremtid # få

Vi kan også bruke metode for a Framtid i stedet for å bruke en Timer:

ExecutorService executor = Executors.newSingleThreadExecutor (); Fremtidig fremtid = executor.submit (ny LongRunningTask ()); prøv {f.get (30, TimeUnit.SECONDS); } fange (TimeoutException e) {f.cancel (true); } til slutt {service.shutdownNow (); }

Her brukte vi ExecutorService å sende inn arbeidertråden som returnerer en forekomst av Framtid, hvem sin metoden vil blokkere hovedtråden til den angitte tiden. Det vil heve en TimeoutException etter den angitte tidsavbruddet. I å fange blokk, avbryter vi arbeidertråden ved å ringe til Avbryt metoden på Future gjenstand.

Den største fordelen med denne tilnærmingen i forhold til den forrige er at den bruker et basseng for å administrere tråden, mens Timer bruker bare en enkelt tråd (ikke basseng).

3.3. Bruker en PlanlagtExcecutorSercvice

Vi kan også bruke PlanlagtExecutorService for å avbryte oppgaven. Denne klassen er en utvidelse av en ExecutorService og gir den samme funksjonaliteten med tillegg av flere metoder som omhandler planlegging av utførelse. Dette kan utføre den gitte oppgaven etter en viss forsinkelse av angitte tidsenheter:

ScheduledExecutorService executor = Executors.newScheduledThreadPool (2); Fremtidig fremtid = executor.submit (ny LongRunningTask ()); executor.schedule (new Runnable () {public void run () {future.cancel (true);}}, 1000, TimeUnit.MILLISECONDS); executor.shutdown ();

Her opprettet vi en planlagt trådgruppe av størrelse to med metoden newScheduledThreadPool. De PlanlagtExecutorService #rute metoden tar en Kjørbar, en forsinkelsesverdi og enheten for forsinkelsen.

Ovennevnte program planlegger oppgaven som skal utføres etter ett sekund fra innleveringstidspunktet. Denne oppgaven vil avbryte den opprinnelige langvarige oppgaven.

Merk at i motsetning til den forrige tilnærmingen, blokkerer vi ikke hovedtråden ved å ringe Fremtid # få metode. Derfor, det er den mest foretrukne tilnærmingen blant alle de ovennevnte tilnærmingene.

4. Er det en garanti?

Det er ingen garanti for at utførelsen stoppes etter en viss tid. Hovedårsaken er at ikke alle blokkeringsmetoder er avbrytbare. Faktisk er det bare noen få veldefinerte metoder som er avbrytbare. Så, hvis en tråd blir avbrutt og et flagg er satt, vil ingenting annet skje før den når en av disse avbrytbare metodene.

For eksempel, lese og skrive metodene er avbrytbare bare hvis de påberopes på strømmer opprettet med en InterruptibleChannel. BufferedReader er ikke en InterruptibleChannel. Så hvis tråden bruker den til å lese en fil, ringer avbryte() på denne tråden blokkert i lese metoden har ingen effekt.

Vi kan imidlertid eksplisitt se etter avbruddsflagget etter hver lesning i en løkke. Dette vil gi en rimelig sikkerhet for å stoppe tråden med en viss forsinkelse. Men dette garanterer ikke å stoppe tråden etter en streng tid, fordi vi ikke vet hvor lang tid en leseoperasjon kan ta.

På den annen side, den vente metoden for Gjenstand klasse er avbrytbar. Dermed ble tråden blokkert i vente metoden vil umiddelbart kaste en InterruptedException etter at avbruddsflagget er satt.

Vi kan identifisere blokkeringsmetodene ved å lete etter en kasterInterruptedException i deres metodesignaturer.

Et viktig råd er å unngå å bruke de foreldede Thread.stop () metode. Å stoppe tråden får den til å låse opp alle skjermene den har låst. Dette skjer på grunn av TrådDød unntak som forplantes oppover bunken.

Hvis noen av gjenstandene som tidligere var beskyttet av disse skjermene, var i en inkonsekvent tilstand, blir de inkonsekvente objektene synlige for andre tråder. Dette kan føre til vilkårlig oppførsel som er veldig vanskelig å oppdage og resonnere om.

5. Konklusjon

I denne opplæringen har vi lært forskjellige teknikker for å stoppe utførelsen etter en gitt tid, sammen med fordeler og ulemper med hver. Den komplette kildekoden finner du på GitHub.


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