CodeGym /Java blog /Tilfældig /Multithreading i Java
John Squirrels
Niveau
San Francisco

Multithreading i Java

Udgivet i gruppen
Hej! Først og fremmest, tillykke: du er nået til emnet Multithreading i Java! Dette er en seriøs præstation - du er nået langt. Men forbered dig selv: dette er et af de sværeste emner på kurset. Og det er ikke fordi vi bruger komplekse klasser eller mange metoder her: faktisk vil vi bruge mindre end tyve. Det er mere, at du bliver nødt til at ændre den måde, du tænker lidt på. Tidligere er dine programmer blevet eksekveret sekventielt. Nogle linjer kode kom efter andre, nogle metoder kom efter andre, og alt var grundlæggende klart. Først beregnede vi noget, viste derefter resultatet på konsollen, og derefter sluttede programmet. For at forstå multithreading er det bedre at tænke i parallelitet. Lad os starte med noget ganske simpelt: ) Forestil dig, at din familie flytter fra et hus til et andet. At samle alle dine bøger vil være en vigtig del af flytningen. Du har samlet en masse bøger, og du skal lægge dem i kasser. I øjeblikket er du den eneste ledige. Mor laver mad, bror pakker tøj, og søster gik i butikken. Alene kan du klare dig på en eller anden måde. Før eller siden vil du selv løse opgaven, men det vil tage meget tid. Din søster kommer dog tilbage fra butikken om 20 minutter, og hun har ikke andet at lave. Så hun kan slutte sig til dig. Opgaven har ikke ændret sig: læg bøger i kasser. Men det bliver udført dobbelt så hurtigt. Hvorfor? For arbejdet foregår sideløbende. To forskellige 'tråde' (dig og din søster) udfører den samme opgave samtidigt. Og hvis intet ændrer sig, så vil der være en kæmpe tidsforskel i forhold til situationen, hvor du gør alting selv. Hvis bror snart er færdig med sit job, kan han hjælpe dig, og tingene vil gå endnu hurtigere.

Problemer løst ved multithreading

Multithreading blev faktisk opfundet for at nå to vigtige mål:
  1. Gør flere ting på samme tid.

    I eksemplet ovenfor udførte forskellige tråde (familiemedlemmer) flere handlinger parallelt: de vaskede op, gik i butikken og pakkede ting.

    Vi kan tilbyde et eksempel, der er tættere relateret til programmering. Antag, at du har et program med en brugergrænseflade. Når du klikker på 'Fortsæt' i programmet, skulle der ske nogle beregninger, og brugeren skulle se følgende skærmbillede. Hvis disse handlinger blev udført sekventielt, ville programmet bare hænge, ​​efter at brugeren har klikket på knappen 'Fortsæt'. Brugeren vil se skærmen med knappen 'Fortsæt', indtil programmet udfører alle interne beregninger og når den del, hvor brugergrænsefladen er opdateret.

    Nå, jeg tror, ​​vi venter et par minutter!

    Multithreading i Java: hvad det er, dets fordele og almindelige faldgruber - 3

    Eller vi kunne omarbejde vores program, eller, som programmører siger, 'parallelle' det. Lad os udføre vores beregninger på en tråd og tegne brugergrænsefladen på en anden. De fleste computere har ressourcer nok til at gøre dette. Hvis vi tager denne rute, så fryser programmet ikke fast, og brugeren vil bevæge sig jævnt mellem skærme uden at bekymre sig om, hvad der sker indeni. Det ene forstyrrer ikke det andet :)

  2. Udfør beregninger hurtigere.

    Alt er meget enklere her. Hvis vores processor har flere kerner, og det har de fleste processorer i dag, så kan flere kerner håndtere vores liste over opgaver parallelt. Det er klart, at hvis vi skal udføre 1000 opgaver og hver tager et sekund, kan en kerne afslutte listen på 1000 sekunder, to kerner på 500 sekunder, tre på lidt mere end 333 sekunder osv.

Men som du allerede har læst i denne lektion, er dagens systemer meget smarte, og på selv én computerkerne er de i stand til at opnå parallelisme, eller rettere pseudo-parallelisme, hvor opgaver udføres på skift. Lad os flytte almindeligheder til detaljer og lære den vigtigste klasse i Java multithreading-biblioteket at kende — java.lang.Thread. Strengt taget er Java-tråde repræsenteret af forekomster af Thread- klassen. Det betyder, at for at oprette og køre 10 tråde, skal du bruge 10 forekomster af denne klasse. Lad os skrive det enkleste eksempel:

public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("I'm Thread! My name is " + getName());
   }
}
For at oprette og køre tråde skal vi oprette en klasse, få den til at arve java.lang . Trådklasse , og tilsidesæt dens run()- metode. Det sidste krav er meget vigtigt. Det er i run() -metoden, vi definerer logikken for, at vores tråd skal udføres. Hvis vi nu opretter og kører en instans af MyFirstThread , vil run() -metoden vise en linje med et navn: getName()- metoden viser trådens 'system'-navn, som tildeles automatisk. Men hvorfor taler vi foreløbigt? Lad os oprette en og finde ud af det!

public class Main {

   public static void main(String[] args) {

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

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Konsoludgang: Jeg er tråd! Mit navn er Tråd-2 Jeg er Tråd! Mit navn er Tråd-1 Jeg er Tråd! Mit navn er Tråd-0 Jeg er Tråd! Mit navn er tråd-3 jeg er tråd! Mit navn er Thread-6 I'm Thread! Mit navn er Thread-7 I'm Thread! Mit navn er Thread-4 I'm Thread! Mit navn er Tråd-5 Jeg er Tråd! Mit navn er Thread-9 I'm Thread! Mit navn er Thread-8 Lad os oprette 10 tråde ( MyFirstThread- objekter, som arver Thread ) og starte dem ved at kalde start()- metoden på hvert objekt. Efter at have kaldt start()- metoden, udføres logikken i run() -metoden. Bemærk: trådnavnene er ikke i orden. Det er mærkeligt, at de ikke var sekventielt:, Tråd-1 , Tråd-2 og så videre? Som det sker, er dette et eksempel på en tid, hvor 'sekventiel' tænkning ikke passer. Problemet er, at vi kun har givet kommandoer til at oprette og køre 10 tråde. Trådplanlæggeren, en speciel operativsystemmekanisme, bestemmer deres udførelsesrækkefølge. Dets præcise design og beslutningsstrategi er emner for en dyb diskussion, som vi ikke vil dykke ned i lige nu. Det vigtigste at huske er, at programmøren ikke kan kontrollere udførelsesrækkefølgen af ​​tråde. For at forstå alvoren af ​​situationen, prøv at køre main()-metoden i eksemplet ovenfor et par gange mere. Konsoludgang ved anden kørsel: Jeg er tråd! Mit navn er Tråd-0 Jeg er Tråd! Mit navn er Thread-4 I'm Thread! Mit navn er tråd-3 jeg er tråd! Mit navn er Tråd-2 Jeg er Tråd! Mit navn er Tråd-1 Jeg er Tråd! Mit navn er Tråd-5 Jeg er Tråd! Mit navn er Thread-6 I'm Thread! Mit navn er Thread-8 I'm Thread! Mit navn er Thread-9 I'm Thread! Mit navn er Thread-7 Console output fra tredje kørsel: I'm Thread! Mit navn er Tråd-0 Jeg er Tråd! Mit navn er tråd-3 jeg er tråd! Mit navn er Tråd-1 Jeg er Tråd! Mit navn er Tråd-2 Jeg er Tråd! Mit navn er Thread-6 I'm Thread! Mit navn er Thread-4 I'm Thread! Mit navn er Thread-9 I'm Thread! Mit navn er Tråd-5 Jeg er Tråd! Mit navn er Thread-7 I'm Thread! Mit navn er tråd-8

Problemer skabt af multithreading

I vores eksempel med bøger så du, at multithreading løser meget vigtige opgaver og kan gøre vores programmer hurtigere. Ofte mange gange hurtigere. Men multithreading anses for at være et vanskeligt emne. Faktisk, hvis det bruges forkert, skaber det problemer i stedet for at løse dem. Når jeg siger 'skaber problemer', mener jeg ikke i en eller anden abstrakt forstand. Der er to specifikke problemer, som multithreading kan skabe: dødvande og racerforhold. Deadlock er en situation, hvor flere tråde venter på ressourcer, der holdes af hinanden, og ingen af ​​dem kan fortsætte med at køre. Vi vil tale mere om det i de efterfølgende lektioner. Følgende eksempel vil være tilstrækkeligt for nu: Multithreading i Java: hvad det er, dets fordele og almindelige faldgruber - 4Forestil dig, at Tråd-1 interagerer med noget Objekt-1, og at Tråd-2 interagerer med Objekt-2. Desuden er programmet skrevet således, at:
  1. Tråd-1 stopper med at interagere med Objekt-1 og skifter til Objekt-2, så snart Tråd-2 stopper med at interagere med Objekt-2 og skifter til Objekt-1.
  2. Tråd-2 stopper med at interagere med Objekt-2 og skifter til Objekt-1, så snart Tråd-1 stopper med at interagere med Objekt-1 og skifter til Objekt-2.
Selv uden en dyb forståelse af multithreading, kan du nemt se, at der ikke sker noget. Trådene vil aldrig skifte plads og vil vente på hinanden for evigt. Fejlen virker åbenlys, men det er den i virkeligheden ikke. Det kan du nemt gøre i et program. Vi vil overveje eksempler på kode, der forårsager dødvande i efterfølgende lektioner. Forresten, Quora har et godt eksempel fra det virkelige liv , der forklarer, hvilken dødvandeer. 'I nogle stater i Indien vil de ikke sælge dig landbrugsjord, medmindre du er en registreret landmand. De vil dog ikke registrere dig som landmand, hvis du ikke ejer landbrugsjord«. Store! Hvad kan vi sige?! :) Lad os nu tale om raceforhold. En race condition er en designfejl i et multithreaded system eller applikation, hvor driften af ​​systemet eller applikationen afhænger af den rækkefølge, som dele af koden udføres i. Husk vores eksempel, hvor vi startede tråde:

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();
       }
   }
}
Forestil dig nu, at programmet er ansvarligt for at køre en robot, der laver mad! Tråd-0 får æg ud af køleskabet. Tråd-1 tænder for komfuret. Tråd-2 får en pande og sætter den på komfuret. Tråd-3 tænder ovnen. Tråd-4 hælder olie i gryden. Tråd-5 knækker æggene og hælder dem i gryden. Tråd-6 smider æggeskallerne i skraldespanden. Tråd-7 fjerner de kogte æg fra brænderen. Tråd-8 lægger de kogte æg på en tallerken. Tråd-9 vasker op. Se på resultaterne af vores program: Tråd udført: Tråd-0 Tråd udført: Tråd-2 Tråd udført Tråd-1 Tråd udført: Tråd-4 Tråd udført: Tråd-9 Tråd udført: Tråd-5 Tråd udført: Tråd-8 Tråd udført: Tråd-7 Tråd udført: Tråd-3 Er dette en komedie rutine? :) Og alt sammen fordi vores programs arbejde afhænger af udførelsesrækkefølgen af ​​trådene. Givet den mindste overtrædelse af den krævede sekvens, bliver vores køkken til et helvede, og en sindssyg robot ødelægger alt omkring det. Dette er også et almindeligt problem i flertrådsprogrammering. Du vil høre om det mere end én gang. Som afslutning på denne lektion vil jeg gerne anbefale en bog om multithreading. Multithreading i Java: hvad det er, dets fordele og almindelige faldgruber - 6'Java Concurrency in Practice' blev skrevet i 2006, men har ikke mistet sin relevans. Den er dedikeret til multithreaded Java-programmering - fra det grundlæggende til de mest almindelige fejl og antimønstre. Hvis du en dag beslutter dig for at blive en multithreading-guru, er denne bog et must-read. Vi ses i de næste lektioner! :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION