Programmering

Introduksjon til Java-tråder

Denne artikkelen, en av de første som noensinne er publisert av JavaWorld, beskriver hvordan tråder implementeres i Java-programmeringsspråket, og starter med en generell oversikt over tråder.

Enkelt sagt, a tråd er et programs gjennomføringsvei. De fleste programmer skrevet i dag kjører som en tråd, og forårsaker problemer når flere hendelser eller handlinger må skje samtidig. La oss si at for eksempel et program ikke er i stand til å tegne bilder mens du leser tastetrykk. Programmet må være fullstendig oppmerksom på tastaturinngangen som ikke har evnen til å håndtere mer enn en hendelse om gangen. Den ideelle løsningen på dette problemet er sømløs kjøring av to eller flere seksjoner av et program samtidig. Tråder lar oss gjøre dette.

Lære om Java-tråder

Denne artikkelen er en del av JavaWorlds tekniske innholdsarkiv. Se følgende for å lære mer om Java-tråder og samtidighet:

Forstå Java-tråder (Java 101 serie, 2002):

  • Del 1: Introduksjon av tråder og løpere
  • Del 2: Trådesynkronisering
  • Del 3: Trådplanlegging og vent / varsle
  • Del 4: Trådgrupper og volatilitet

Relaterte artikler

  • Hyper-threaded Java: Bruk av Java Concurrency API (2006)
  • Bedre skjermer for flertrådede programmer (2007)
  • Forståelse av skuespillerens samtidighet, del 1 (2009)
  • Hengende gjenkjenning og håndtering (2011)

Sjekk også JavaWorld nettstedskart og søkemotor.

Flertrådede applikasjoner leverer sin kraftige kraft ved å kjøre mange tråder samtidig i et enkelt program. Fra et logisk synspunkt betyr multitrading at flere linjer i et enkelt program kan utføres samtidig, men det er ikke det samme som å starte et program to ganger og si at det er flere linjer i et program som blir utført samtidig tid. I dette tilfellet behandler operativsystemet programmene som to separate og forskjellige prosesser. Under Unix skaper forking av en prosess en underordnet prosess med en annen adresseplass for både kode og data. Derimot, gaffel() skaper mye overhead for operativsystemet, noe som gjør det til en veldig CPU-intensiv operasjon. Ved å starte en tråd i stedet opprettes en effektiv kjøringsvei mens den fremdeles deler det opprinnelige dataområdet fra den overordnede. Ideen om å dele dataområdet er veldig gunstig, men tar opp noen bekymringsområder som vi vil diskutere senere.

Opprette tråder

Java-skaperne har nådig designet to måter å lage tråder på: implementere et grensesnitt og utvide en klasse. Å utvide en klasse er måten Java arver metoder og variabler fra en overordnet klasse. I dette tilfellet kan man bare utvide eller arve fra en enslig forelderklasse. Denne begrensningen innen Java kan overvinnes ved å implementere grensesnitt, som er den vanligste måten å lage tråder på. (Merk at arvingen bare gjør at klassen kan kjøres som en tråd. Det er opp til klassen å start() henrettelse osv.)

Grensesnitt gir en måte for programmerere å legge grunnlaget for en klasse. De brukes til å designe kravene for et sett med klasser å implementere. Grensesnittet setter opp alt, og klassen eller klassene som implementerer grensesnittet gjør alt arbeidet. De forskjellige klassesettene som implementerer grensesnittet, må følge de samme reglene.

Det er noen forskjeller mellom en klasse og et grensesnitt. For det første kan et grensesnitt bare inneholde abstrakte metoder og / eller statiske sluttvariabler (konstanter). Klasser kan derimot implementere metoder og inneholde variabler som ikke er konstanter. For det andre kan et grensesnitt ikke implementere noen metoder. En klasse som implementerer et grensesnitt, må implementere alle metodene som er definert i det grensesnittet. Et grensesnitt har muligheten til å utvide seg fra andre grensesnitt, og (i motsetning til klasser) kan det strekke seg fra flere grensesnitt. Videre kan et grensesnitt ikke instantieres med den nye operatøren; for eksempel, Runnable a = new Runnable (); er ikke lov.

Den første metoden for å lage en tråd er å bare strekke seg fra Tråd klasse. Gjør dette bare hvis klassen du trenger å utføre som en tråd, aldri trenger å utvides fra en annen klasse. De Tråd klasse er definert i pakken java.lang, som må importeres slik at klassene våre er klar over definisjonen.

importer java.lang. *; public class Counter utvider tråd {public void run () {....}}

Ovennevnte eksempel skaper en ny klasse Disk som utvider Tråd klasse og overstyrer Thread.run () metode for egen implementering. De løpe() metoden er der alt arbeidet til Disk klassetråden er ferdig. Samme klasse kan opprettes ved å implementere Runnable:

importer java.lang. *; offentlig klasse Telleredskaper Runnable {Thread T; offentlig tomkjøring () {....}}

Her, det abstrakte løpe() metoden er definert i Runnable-grensesnittet og implementeres. Merk at vi har en forekomst av Tråd klasse som en variabel av Disk klasse. Den eneste forskjellen mellom de to metodene er at ved å implementere Runnable er det større fleksibilitet i opprettelsen av klassen Disk. I eksemplet ovenfor eksisterer fremdeles muligheten til å utvide Disk klasse, om nødvendig. Flertallet av klasser som er opprettet som må kjøres som en tråd, vil implementere Runnable siden de sannsynligvis utvider noen annen funksjonalitet fra en annen klasse.

Ikke tro at det Runnable-grensesnittet gjør noe reelt arbeid når tråden blir utført. Det er bare en klasse laget for å gi en idé om utformingen av Tråd klasse. Faktisk er det veldig lite som bare inneholder en abstrakt metode. Her er definisjonen av det Runnable-grensesnittet direkte fra Java-kilden:

pakke java.lang; offentlig grensesnitt Runnable {public abstract void run (); }

Det er alt det er til det Runnable-grensesnittet. Et grensesnitt gir bare et design som klasser skal implementeres på. Når det gjelder Runnable-grensesnittet, tvinger det definisjonen av bare løpe() metode. Derfor er det meste av arbeidet gjort i Tråd klasse. En nærmere titt på et avsnitt i definisjonen av Tråd klassen vil gi en ide om hva som egentlig skjer:

public class Tråd implementerer Runnable {... public void run () {if (target! = null) {target.run (); }} ...}

Fra kodebiten ovenfor er det tydelig at trådklassen også implementerer Runnable-grensesnittet. Tråd.løpe() kontrollerer for å sikre at målklassen (klassen som skal kjøres som en tråd) ikke er lik null, og deretter utfører løpe() metode for målet. Når dette skjer, vil løpe() metoden for målet vil kjøre som sin egen tråd.

Starter og stopper

Siden de forskjellige måtene å lage en forekomst av en tråd nå er tydelige, vil vi diskutere implementeringen av tråder som begynner med måtene som er tilgjengelige for å starte og stoppe dem ved hjelp av en liten applet som inneholder en tråd for å illustrere mekanikken:

CounterThread eksempel og kildekode

Ovenstående applet begynner å telle fra 0 og viser utdataene til både skjermen og konsollen. Et raskt blikk kan gi inntrykk av at programmet begynner å telle og vise hvert tall, men dette er ikke tilfelle. En nærmere undersøkelse av gjennomføringen av denne appleten vil avsløre dens sanne identitet.

I dette tilfellet Mottråd klasse ble tvunget til å implementere Runnable siden den utvidet klassen Applet. Som i alle appletter, er i det() metoden blir utført først. I i det(), blir variabelen Count initialisert til null og en ny forekomst av Tråd klasse er opprettet. Ved å passere dette til Tråd konstruktør, vil den nye tråden vite hvilket objekt som skal kjøres. I dette tilfellet dette er en referanse til Mottråd. Etter at tråden er opprettet, må den startes. Oppfordringen til start() vil kalle målets løpe() metode, som er Mottråd.løpe(). Oppfordringen til start() kommer tilbake med en gang og tråden begynner å kjøres samtidig. Merk at løpe() metoden er en uendelig løkke. Det er uendelig fordi når den løpe() metode avsluttes, slutter tråden å kjøre. De løpe() metoden vil øke variabelen Count, sove i 10 millisekunder og sende en forespørsel om å oppdatere appletens skjerm.

Merk at det er viktig å sove et sted i en tråd. Hvis ikke, bruker tråden all CPU-tid for prosessen og tillater ikke at andre metoder som tråder blir utført. En annen måte å slutte å utføre en tråd på er å ringe Stoppe() metode. I dette eksemplet stopper tråden når du trykker på musen mens markøren er i appleten. Avhengig av datamaskinens hastighet appleten kjører på, vil ikke alle tall vises, fordi inkrementeringen gjøres uavhengig av malingen av appleten. Appleten kan ikke oppdateres ved hver forespørsel, så operativsystemet vil stille forespørslene i kø, og påfølgende oppdateringsforespørsler vil bli fornøyd med en oppdatering. Mens oppdateringene står i kø, økes tellingen, men vises ikke.

Avbryte og gjenoppta

Når en tråd er stoppet, kan den ikke startes på nytt med start() kommando, siden Stoppe() vil avslutte utførelsen av en tråd. I stedet kan du stoppe utførelsen av en tråd med søvn() metode. Tråden vil sove i en viss periode og begynne å kjøre når fristen er nådd. Men dette er ikke ideelt hvis tråden må startes når en bestemt hendelse inntreffer. I dette tilfellet utsette() metoden lar en tråd midlertidig slutte å kjøre og gjenoppta() metoden lar den suspenderte tråden starte på nytt. Følgende applet viser eksemplet ovenfor modifisert for å suspendere og gjenoppta appleten.

offentlig klasse CounterThread2 utvider Applet implementerer Runnable {Thread t; int Count; boolsk suspendert; public boolean mouseDown (Event e, int x, int y) {if (suspendert) t.resume (); annet t.suspend (); suspendert =! suspendert; returner sant; } ...}

CounterThread2 Eksempel og kildekode

For å holde oversikt over den nåværende tilstanden til appleten, den boolske variabelen suspendert benyttes. Å skille de forskjellige tilstandene til en applet er viktig fordi noen metoder vil gi unntak hvis de kalles i feil tilstand. For eksempel, hvis appleten er startet og stoppet, kjører du start() metoden vil kaste en IllegalThreadStateException unntak.

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