CodeGym /Java-blogg /Tilfeldig /Multithreading i Java
John Squirrels
Nivå
San Francisco

Multithreading i Java

Publisert i gruppen
Hei! Først av alt, gratulerer: du har nådd emnet Multithreading i Java! Dette er en seriøs prestasjon - du har kommet langt. Men forbered deg: dette er et av de vanskeligste temaene i kurset. Og det er ikke det at vi bruker komplekse klasser eller mange metoder her: faktisk vil vi bruke mindre enn tjue. Det er mer at du må endre hvordan du tenker litt. Tidligere har programmene dine blitt kjørt sekvensielt. Noen kodelinjer kom etter andre, noen metoder kom etter andre, og alt var i grunnen klart. Først regnet vi ut noe, viste deretter resultatet på konsollen, og så ble programmet avsluttet. For å forstå multithreading, er det bedre å tenke i termer av parallellitet. La oss starte med noe ganske enkelt: ) Tenk deg at familien din flytter fra ett hus til et annet. Å samle alle bøkene dine vil være en viktig del av flyttingen. Du har samlet mange bøker, og du må legge dem i esker. For øyeblikket er du den eneste tilgjengelige. Mamma lager mat, bror pakker klær, og søster dro til butikken. Alene kan du klare deg på en eller annen måte. Før eller siden vil du fullføre oppgaven selv, men det vil ta mye tid. Søsteren din kommer imidlertid tilbake fra butikken om 20 minutter, og hun har ikke noe annet å gjøre. Så hun kan bli med deg. Oppgaven har ikke endret seg: legg bøker i bokser. Men det blir utført dobbelt så raskt. Hvorfor? For arbeidet skjer parallelt. To forskjellige 'tråder' (du og din søster) utfører samme oppgave samtidig. Og hvis ingenting endres, da blir det en enorm tidsforskjell sammenlignet med situasjonen der du gjør alt selv. Hvis bror snart er ferdig med jobben sin, kan han hjelpe deg og ting vil gå enda raskere.

Problemer løst ved multithreading

Multithreading ble faktisk oppfunnet for å oppnå to viktige mål:
  1. Gjør flere ting samtidig.

    I eksemplet ovenfor utførte forskjellige tråder (familiemedlemmer) flere handlinger parallelt: de vasket opp, gikk til butikken og pakket ting.

    Vi kan gi et eksempel som er nærmere knyttet til programmering. Anta at du har et program med et brukergrensesnitt. Når du klikker 'Fortsett' i programmet, bør noen beregninger skje og brukeren skal se følgende skjermbilde. Hvis disse handlingene ble utført sekvensielt, ville programmet bare henge etter at brukeren klikker på "Fortsett"-knappen. Brukeren vil se skjermen med "Fortsett"-knappskjermen til programmet utfører alle interne beregninger og når delen der brukergrensesnittet er oppdatert.

    Vel, jeg antar at vi venter et par minutter!

    Multithreading i Java: hva det er, fordelene og vanlige fallgruvene - 3

    Eller vi kan omarbeide programmet vårt, eller, som programmerere sier, 'parallisere' det. La oss utføre våre beregninger på en tråd og tegne brukergrensesnittet på en annen. De fleste datamaskiner har nok ressurser til å gjøre dette. Hvis vi tar denne ruten, vil ikke programmet fryse og brukeren vil bevege seg jevnt mellom skjermene uten å bekymre seg for hva som skjer inni. Det ene forstyrrer ikke det andre :)

  2. Utfør beregninger raskere.

    Alt er mye enklere her. Hvis prosessoren vår har flere kjerner, og det har de fleste prosessorer i dag, så kan flere kjerner håndtere oppgavelisten vår parallelt. Selvfølgelig, hvis vi trenger å utføre 1000 oppgaver og hver tar ett sekund, kan en kjerne fullføre listen på 1000 sekunder, to kjerner på 500 sekunder, tre på litt mer enn 333 sekunder, osv.

Men som du allerede har lest i denne leksjonen, er dagens systemer veldig smarte, og på til og med én datakjerne er i stand til å oppnå parallellisme, eller rettere sagt pseudo-parallelisme, hvor oppgaver utføres vekselvis. La oss flytte generelle til spesifikke og bli kjent med den viktigste klassen i Java multithreading-biblioteket - java.lang.Thread. Strengt tatt er Java-tråder representert av forekomster av Thread- klassen. Dette betyr at for å opprette og kjøre 10 tråder, trenger du 10 forekomster av denne klassen. La oss skrive det enkleste eksemplet:

public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("I'm Thread! My name is " + getName());
   }
}
For å opprette og kjøre tråder, må vi opprette en klasse, få den til å arve java.lang . trådklasse , og overstyr dens run()- metode. Det siste kravet er veldig viktig. Det er i run() -metoden vi definerer logikken for at tråden vår skal utføres. Nå, hvis vi oppretter og kjører en forekomst av MyFirstThread , vil run() -metoden vise en linje med et navn: getName()- metoden viser trådens 'system'-navn, som tildeles automatisk. Men hvorfor snakker vi forsøksvis? La oss lage en og finne ut!

public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Konsollutgang: I'm Thread! Mitt navn er Thread-2 I'm Thread! Mitt navn er tråd-1 jeg er tråd! Mitt navn er Tråd-0 Jeg er Tråd! Mitt navn er Thread-3 I'm Thread! Mitt navn er Thread-6 I'm Thread! Mitt navn er Thread-7 I'm Thread! Mitt navn er Thread-4 I'm Thread! Mitt navn er Thread-5 I'm Thread! Mitt navn er Thread-9 I'm Thread! Mitt navn er Thread-8 La oss lage 10 tråder ( MyFirstThread- objekter, som arver Thread ) og starte dem ved å kalle start()- metoden på hvert objekt. Etter å ha kalt start()- metoden, utføres logikken i run() -metoden. Merk: trådnavnene er ikke i orden. Det er rart at de ikke var sekvensielt:, Tråd-1 , Tråd-2 og så videre? Som det skjer, er dette et eksempel på en tid da "sekvensiell" tenkning ikke passer. Problemet er at vi bare har gitt kommandoer for å opprette og kjøre 10 tråder. Trådplanleggeren, en spesiell operativsystemmekanisme, bestemmer utførelsesrekkefølgen deres. Dens presise design og beslutningsstrategi er temaer for en dyp diskusjon som vi ikke vil dykke ned i akkurat nå. Det viktigste å huske er at programmereren ikke kan kontrollere utførelsesrekkefølgen til tråder. For å forstå alvoret i situasjonen, prøv å kjøre main()-metoden i eksemplet ovenfor et par ganger til. Konsollutgang ved andre kjøring: Jeg er tråden! Mitt navn er Tråd-0 Jeg er Tråd! Mitt navn er Thread-4 I'm Thread! Mitt navn er Thread-3 I'm Thread! Mitt navn er Thread-2 I'm Thread! Mitt navn er tråd-1 jeg er tråd! Mitt navn er Thread-5 I'm Thread! Mitt navn er Thread-6 I'm Thread! Mitt navn er Thread-8 I'm Thread! Mitt navn er Thread-9 I'm Thread! Mitt navn er Thread-7 Console-utgang fra den tredje kjøringen: I'm Thread! Mitt navn er Tråd-0 Jeg er Tråd! Mitt navn er Thread-3 I'm Thread! Mitt navn er tråd-1 jeg er tråd! Mitt navn er Thread-2 I'm Thread! Mitt navn er Thread-6 I'm Thread! Mitt navn er Thread-4 I'm Thread! Mitt navn er Thread-9 I'm Thread! Mitt navn er Thread-5 I'm Thread! Mitt navn er Thread-7 I'm Thread! Mitt navn er Thread-8

Problemer skapt av multithreading

I vårt eksempel med bøker så du at multithreading løser svært viktige oppgaver og kan gjøre programmene våre raskere. Ofte mange ganger raskere. Men multithreading anses å være et vanskelig tema. Faktisk, hvis det brukes feil, skaper det problemer i stedet for å løse dem. Når jeg sier 'skaper problemer', mener jeg ikke i noen abstrakt forstand. Det er to spesifikke problemer som multithreading kan skape: dødlås og løpsforhold. Deadlock er en situasjon der flere tråder venter på ressurser som holdes av hverandre, og ingen av dem kan fortsette å kjøre. Vi vil snakke mer om det i de påfølgende leksjonene. Følgende eksempel vil være tilstrekkelig for nå: Multithreading i Java: hva det er, fordelene og vanlige fallgruvene - 4Tenk deg at Tråd-1 samhandler med noe Objekt-1, og at Tråd-2 samhandler med Objekt-2. Videre er programmet skrevet slik at:
  1. Tråd-1 slutter å samhandle med Objekt-1 og bytter til Objekt-2 så snart Tråd-2 slutter å samhandle med Objekt-2 og bytter til Objekt-1.
  2. Tråd-2 slutter å samhandle med Objekt-2 og bytter til Objekt-1 så snart Tråd-1 slutter å samhandle med Objekt-1 og bytter til Objekt-2.
Selv uten en dyp forståelse av multithreading, kan du lett se at ingenting vil skje. Trådene vil aldri bytte plass og vil vente på hverandre for alltid. Feilen virker åpenbar, men i virkeligheten er den ikke det. Dette kan du enkelt gjøre i et program. Vi vil vurdere eksempler på kode som forårsaker vranglås i påfølgende leksjoner. Forresten, Quora har et flott eksempel fra virkeligheten som forklarer hva som er dødter. «I noen stater i India vil de ikke selge deg jordbruksland med mindre du er en registrert bonde. De vil imidlertid ikke registrere deg som bonde hvis du ikke eier landbruksareal. Flott! Hva kan vi si?! :) La oss nå snakke om løpsforholdene. En løpsbetingelse er en designfeil i et multithreaded system eller applikasjon, hvor driften av systemet eller applikasjonen avhenger av rekkefølgen som deler av koden utføres i. Husk eksempelet vårt der vi startet tråder:

public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("Thread executed: " + getName());
   }
}

public class Main {

   public static void main(String[] args) {

       for (int i = 0; i < 10; i++) {

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Tenk deg nå at programmet er ansvarlig for å drive en robot som lager mat! Tråd-0 får egg ut av kjøleskapet. Tråd-1 slår på komfyren. Tråd-2 får en panne og setter den på komfyren. Tråd-3 tenner ovnen. Tråd-4 heller olje i pannen. Tråd-5 knekker eggene og heller dem i pannen. Tråd-6 kaster eggeskallene i søppelbøtta. Tråd-7 fjerner de kokte eggene fra brenneren. Tråd-8 legger de kokte eggene på en tallerken. Tråd-9 vasker oppvasken. Se på resultatene av programmet vårt: Tråd utført: Tråd-0 Tråd utført: Tråd-2 Tråd utført Tråd-1 Tråd utført: Tråd-4 Tråd utført: Tråd-9 Tråd utført: Tråd-5 Tråd utført: Tråd-8 Tråd utført: Tråd-7 Tråd utført: Tråd-3 Er dette en komedierutine? :) Og alt fordi programmets arbeid avhenger av utførelsesrekkefølgen til trådene. Gitt det minste brudd på den nødvendige sekvensen, blir kjøkkenet vårt til et helvete, og en sinnssyk robot ødelegger alt rundt det. Dette er også et vanlig problem i flertrådsprogrammering. Du vil høre om det mer enn én gang. Som avslutning på denne leksjonen vil jeg anbefale en bok om multithreading. Multithreading i Java: hva det er, fordelene og vanlige fallgruvene - 6'Java Concurrency in Practice' ble skrevet i 2006, men har ikke mistet sin relevans. Den er dedikert til multithreaded Java-programmering - fra det grunnleggende til de vanligste feilene og antimønstrene. Hvis du en dag bestemmer deg for å bli en multithreading-guru, er denne boken en må-lese. Vi sees i neste leksjoner! :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION