Sådan stoppes udførelsen efter en bestemt tid i Java

1. Oversigt

I denne artikel lærer vi, hvordan vi kan afslutte en langvarig udførelse efter et bestemt tidspunkt. Vi undersøger de forskellige løsninger på dette problem. Vi dækker også nogle af deres faldgruber.

2. Brug af en løkke

Forestil dig, at vi behandler en masse ting i en løkke, såsom nogle detaljer om produktvarerne i en e-handelsapplikation, men at det måske ikke er nødvendigt at udfylde alle varerne.

Faktisk vil vi kun behandle op til et bestemt tidspunkt, og derefter vil vi stoppe udførelsen og vise, hvad listen har behandlet indtil det tidspunkt.

Lad os se et hurtigt eksempel:

lang start = System.currentTimeMillis (); lang ende = start + 30 * 1000; while (System.currentTimeMillis () <end) {// En del dyr handling på varen. }

Her bryder sløjfen, hvis tiden har overskredet grænsen på 30 sekunder. Der er nogle bemærkelsesværdige punkter i ovenstående løsning:

  • Lav nøjagtighed: Sløjfen kan køre længere end den pålagte tidsgrænse. Dette afhænger af den tid, hver iteration kan tage. For eksempel, hvis hver iteration kan tage op til 7 sekunder, kan den samlede tid gå op til 35 sekunder, hvilket er omkring 17% længere end den ønskede tidsgrænse på 30 sekunder
  • Blokering: En sådan behandling i hovedtråden er muligvis ikke en god idé, da den blokerer den i lang tid. I stedet skal disse operationer afkobles fra hovedtråden

I det næste afsnit vil vi diskutere, hvordan den interruptbaserede tilgang eliminerer disse begrænsninger.

3. Brug af en afbrydelsesmekanisme

Her bruger vi en separat tråd til at udføre de langvarige operationer. Hovedtråden sender et afbrydelsessignal til arbejdertråden ved timeout.

Hvis arbejdertråden stadig er i live, fanger den signalet og stopper udførelsen. Hvis arbejdstageren slutter inden timeoutet, har det ingen indflydelse på arbejdertråden.

Lad os se på arbejdertråden:

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

Her, Tråd. Sove simulerer en langvarig operation. I stedet for dette kunne der være enhver anden operation. Det er vigtigt at tjek afbrydelsesflagget, fordi ikke alle operationer kan afbrydes. Så i disse tilfælde skal vi manuelt kontrollere flagget.

Vi bør også kontrollere dette flag i hver iteration for at sikre, at tråden stopper med at udføre sig selv inden for forsinkelsen af ​​en iteration højst.

Derefter dækker vi tre forskellige mekanismer til afsendelse af afbrydelsessignalet.

3.1. Brug af en Timer

Alternativt kan vi oprette en TimerTask for at afbryde arbejderens tråd ved timeout:

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

Her har vi defineret en TimerTask der tager en arbejdstråd på tidspunktet for oprettelsen. Det gør det afbryde arbejderens tråd ved påkaldelse af dens løb metode. Det Timer vil udløse TimerTask efter den angivne forsinkelse:

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

3.2. Brug af metoden Fremtidens # få

Vi kan også bruge metode til en Fremtid i stedet for at bruge en Timer:

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

Her brugte vi ExecutorService at indsende arbejdstråden, der returnerer en forekomst af Fremtid, hvis metode blokerer hovedtråden indtil det angivne tidspunkt. Det vil rejse et TimeoutException efter den angivne timeout. I fangst blokere, afbryder vi arbejdertråden ved at ringe til afbestille metode til Future objekt.

Den største fordel ved denne tilgang i forhold til den foregående er, at den bruger en pool til at styre tråden, mens Timer bruger kun en enkelt tråd (ingen pool).

3.3. Brug af en PlanlagtExcecutorSercvice

Vi kan også bruge ScheduledExecutorService for at afbryde opgaven. Denne klasse er en udvidelse af en ExecutorService og giver den samme funktionalitet med tilføjelse af flere metoder, der beskæftiger sig med planlægning af udførelse. Dette kan udføre den givne opgave efter en vis forsinkelse af indstillede tidsenheder:

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

Her oprettede vi en planlagt trådpulje af størrelse to med metoden newScheduledThreadPool. Det ScheduledExecutorService #tidsplan metoden tager en Kan køres, en forsinkelsesværdi og forsinkelsesenheden.

Ovenstående program planlægger, at opgaven skal udføres efter et sekund fra tidspunktet for indsendelse. Denne opgave annullerer den oprindelige langvarige opgave.

Bemærk, at i modsætning til den tidligere tilgang blokerer vi ikke hovedtråden ved at kalde Fremtidens # få metode. Derfor, det er den mest foretrukne tilgang blandt alle de ovennævnte tilgange.

4. Er der en garanti?

Der er ingen garanti for, at udførelsen stoppes efter et bestemt tidspunkt. Hovedårsagen er, at ikke alle blokeringsmetoder kan afbrydes. Faktisk er der kun et par veldefinerede metoder, der kan afbrydes. Så, hvis en tråd afbrydes, og et flag er sat, sker der intet andet, før det når en af ​​disse afbrydelige metoder.

For eksempel, Læs og skrive metoder kan kun afbrydes, hvis de påberåbes på streams oprettet med en InterruptibleChannel. BufferedReader er ikke en InterruptibleChannel. Så hvis tråden bruger den til at læse en fil, ringer afbryde() på denne tråd blokeret i Læs metoden har ingen virkning.

Vi kan dog eksplicit kontrollere for afbrydelsesflag efter hver læsning i en løkke. Dette vil give en rimelig sikkerhed for at stoppe tråden med en vis forsinkelse. Men dette garanterer ikke at stoppe tråden efter en stram tid, fordi vi ikke ved, hvor meget tid en læseoperation kan tage.

På den anden side er vente metode til Objekt klasse kan afbrydes. Således er tråden blokeret i vente metode vil straks kaste en Afbrudt undtagelse efter at afbrydelsesflagget er indstillet.

Vi kan identificere blokeringsmetoderne ved at lede efter en kasterAfbrudt undtagelse i deres metodesignaturer.

Et vigtigt råd er at undgå at bruge forældede Thread.stop () metode. Stop af tråden får den til at låse op for alle de skærme, den har låst. Dette sker på grund af TrådDød undtagelse, der spredes op ad stakken.

Hvis nogen af ​​de objekter, der tidligere var beskyttet af disse skærme, var i en inkonsekvent tilstand, bliver de inkonsekvente objekter synlige for andre tråde. Dette kan føre til vilkårlig opførsel, der er meget svær at opdage og ræsonnere om.

5. Konklusion

I denne vejledning har vi lært forskellige teknikker til at stoppe udførelsen efter et givet tidspunkt sammen med fordele og ulemper ved hver. Den komplette kildekode kan findes på GitHub.


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