CodeGym /Java blogg /Slumpmässig /Multithreading i Java
John Squirrels
Nivå
San Francisco

Multithreading i Java

Publicerad i gruppen
Hej! Först och främst, grattis: du har nått ämnet Multithreading i Java! Det här är en allvarlig prestation – du har kommit långt. Men förbered dig: det här är ett av de svåraste ämnena i kursen. Och det är inte så att vi använder komplexa klasser eller massor av metoder här: i själva verket kommer vi att använda mindre än tjugo. Det är mer att du måste ändra hur du tänker något. Tidigare har dina program körts sekventiellt. Vissa rader kod kom efter andra, vissa metoder kom efter andra, och allt var i princip tydligt. Först beräknade vi något, visade sedan resultatet på konsolen och sedan avslutades programmet. För att förstå multithreading är det bättre att tänka i termer av parallellism. Låt oss börja med något ganska enkelt: ) Föreställ dig att din familj flyttar från ett hus till ett annat. Att samla alla dina böcker kommer att vara en viktig del av flytten. Du har samlat på dig massor av böcker och du måste lägga dem i lådor. För närvarande är du den enda som är tillgänglig. Mamma lagar mat, bror packar kläder och syster gick till affären. Ensam kan du klara dig på något sätt. Förr eller senare kommer du att slutföra uppgiften själv, men det kommer att ta mycket tid. Din syster kommer dock tillbaka från affären om 20 minuter, och hon har inget annat att göra. Så hon kan gå med dig. Uppgiften har inte förändrats: lägg böcker i lådor. Men det går dubbelt så snabbt. Varför? För arbetet pågår parallellt. Två olika "trådar" (du och din syster) utför samma uppgift samtidigt. Och om ingenting förändras, då blir det en enorm tidsskillnad jämfört med situationen där du gör allt själv. Om brorsan slutar sitt jobb snart kan han hjälpa dig och det kommer att gå ännu snabbare.

Problem lösta genom multithreading

Multithreading uppfanns faktiskt för att uppnå två viktiga mål:
  1. Gör flera saker samtidigt.

    I exemplet ovan utförde olika trådar (familjemedlemmar) flera handlingar parallellt: de diskade, gick till affären och packade saker.

    Vi kan ge ett exempel som är närmare relaterat till programmering. Anta att du har ett program med ett användargränssnitt. När du klickar på 'Fortsätt' i programmet bör vissa beräkningar ske och användaren bör se följande skärm. Om dessa åtgärder utfördes sekventiellt, skulle programmet bara hänga sig efter att användaren klickat på knappen "Fortsätt". Användaren kommer att se skärmen med knappen "Fortsätt" tills programmet utför alla interna beräkningar och når den del där användargränssnittet uppdateras.

    Jag antar att vi väntar ett par minuter!

    Multithreading i Java: vad det är, dess fördelar och vanliga fallgropar - 3

    Eller så kan vi omarbeta vårt program, eller, som programmerare säger, "parallellisera" det. Låt oss utföra våra beräkningar på en tråd och rita användargränssnittet på en annan. De flesta datorer har tillräckligt med resurser för att göra detta. Om vi ​​tar den här vägen kommer programmet inte att frysa och användaren kommer att flytta smidigt mellan skärmarna utan att oroa sig för vad som händer inuti. Det ena stör inte det andra :)

  2. Utför beräkningar snabbare.

    Allt är mycket enklare här. Om vår processor har flera kärnor, och det har de flesta processorer idag, så kan flera kärnor hantera vår lista med uppgifter parallellt. Självklart, om vi behöver utföra 1000 uppgifter och var och en tar en sekund, kan en kärna avsluta listan på 1000 sekunder, två kärnor på 500 sekunder, tre på lite mer än 333 sekunder, etc.

Men som du redan har läst i den här lektionen är dagens system väldigt smarta, och på en och samma datorkärna kan man uppnå parallellism, eller snarare pseudo-parallellism, där uppgifter utförs omväxlande. Låt oss gå över till detaljerna och lära känna den viktigaste klassen i Java multithreading-bibliotek — java.lang.Thread. Strängt taget representeras Java-trådar av instanser av klassen Thread . Det betyder att för att skapa och köra 10 trådar behöver du 10 instanser av den här klassen. Låt oss skriva det enklaste exemplet:

public class MyFirstThread extends Thread {

   @Override
   public void run() {
       System.out.println("I'm Thread! My name is " + getName());
   }
}
För att skapa och köra trådar måste vi skapa en klass, få den att ärva java.lang . Trådklass och åsidosätt dess run() -metod. Det sista kravet är mycket viktigt. Det är i run() -metoden som vi definierar logiken för vår tråd att exekvera. Om vi ​​nu skapar och kör en instans av MyFirstThread , kommer run () -metoden att visa en rad med ett namn: metoden getName() visar trådens "system"-namn, som tilldelas automatiskt. Men varför talar vi trevande? Låt oss skapa en och ta reda på det!

public class Main {

   public static void main(String[] args) {

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

           MyFirstThread thread = new MyFirstThread();
           thread.start();
       }
   }
}
Konsolutgång: Jag är tråden! Jag heter Thread-2 I'm Thread! Jag heter Tråd-1 Jag är Tråd! Jag heter Tråd-0 Jag är Tråd! Jag heter Tråd-3 Jag är Tråd! Jag heter Thread-6 I'm Thread! Jag heter Thread-7 I'm Thread! Jag heter Thread-4 I'm Thread! Jag heter Thread-5 I'm Thread! Jag heter Thread-9 I'm Thread! Mitt namn är Thread-8 Låt oss skapa 10 trådar ( MyFirstThread -objekt, som ärver Thread ) och starta dem genom att anropa start() -metoden på varje objekt. Efter att ha anropat start() -metoden exekveras logiken i run()- metoden. Obs: trådnamnen är inte i ordning. Det är konstigt att de inte var sekventiellt:, Tråd-1 , Tråd-2 och så vidare? Som det händer är detta ett exempel på en tid då "sekventiellt" tänkande inte passar. Problemet är att vi bara har tillhandahållit kommandon för att skapa och köra 10 trådar. Trådschemaläggaren, en speciell operativsystemmekanism, bestämmer deras exekveringsordning. Dess exakta design och beslutsfattande strategi är ämnen för en djupgående diskussion som vi inte kommer att dyka in i just nu. Det viktigaste att komma ihåg är att programmeraren inte kan kontrollera exekveringsordningen för trådar. För att förstå allvaret i situationen, prova att köra main()-metoden i exemplet ovan ett par gånger till. Konsolutgång vid andra körningen: Jag är tråden! Jag heter Tråd-0 Jag är Tråd! Jag heter Thread-4 I'm Thread! Jag heter Tråd-3 Jag är Tråd! Jag heter Thread-2 I'm Thread! Jag heter Tråd-1 Jag är Tråd! Jag heter Thread-5 I'm Thread! Jag heter Thread-6 I'm Thread! Jag heter Thread-8 I'm Thread! Jag heter Thread-9 I'm Thread! Mitt namn är Thread-7 Console-utgång från den tredje körningen: I'm Thread! Jag heter Tråd-0 Jag är Tråd! Jag heter Tråd-3 Jag är Tråd! Jag heter Tråd-1 Jag är Tråd! Jag heter Thread-2 I'm Thread! Jag heter Thread-6 I'm Thread! Jag heter Thread-4 I'm Thread! Jag heter Thread-9 I'm Thread! Jag heter Thread-5 I'm Thread! Jag heter Thread-7 I'm Thread! Jag heter Thread-8

Problem skapade av multithreading

I vårt exempel med böcker såg du att multithreading löser mycket viktiga uppgifter och kan göra våra program snabbare. Ofta många gånger snabbare. Men multithreading anses vara ett svårt ämne. Faktum är att om det används felaktigt skapar det problem istället för att lösa dem. När jag säger "skapar problem", menar jag inte i någon abstrakt mening. Det finns två specifika problem som multithreading kan skapa: dödläge och tävlingsförhållanden. Deadlock är en situation där flera trådar väntar på resurser som hålls av varandra, och ingen av dem kan fortsätta att köras. Vi kommer att prata mer om det i efterföljande lektioner. Följande exempel kommer att räcka för nu: Multithreading i Java: vad det är, dess fördelar och vanliga fallgropar - 4Föreställ dig att Tråd-1 interagerar med något Objekt-1 och att Tråd-2 interagerar med Objekt-2. Dessutom är programmet skrivet så att:
  1. Tråd-1 slutar interagera med Objekt-1 och växlar till Objekt-2 så fort Tråd-2 slutar interagera med Objekt-2 och växlar till Objekt-1.
  2. Tråd-2 slutar interagera med Objekt-2 och växlar till Objekt-1 så fort Tråd-1 slutar interagera med Objekt-1 och växlar till Objekt-2.
Även utan en djup förståelse för multithreading kan du lätt se att ingenting kommer att hända. Trådarna kommer aldrig att byta plats och kommer att vänta på varandra för alltid. Felet verkar uppenbart, men i verkligheten är det inte det. Du kan enkelt göra detta i ett program. Vi kommer att överväga exempel på kod som orsakar dödläge i efterföljande lektioner. Förresten, Quora har ett fantastiskt verkligt exempel som förklarar vilket dödlägeär. "I vissa stater i Indien kommer de inte att sälja jordbruksmark till dig om du inte är en registrerad bonde. Däremot kommer de inte att registrera dig som jordbrukare om du inte äger jordbruksmark”. Bra! Vad kan vi säga?! :) Låt oss nu prata om tävlingsförhållanden. Ett race condition är ett designfel i ett flertrådigt system eller program, där driften av systemet eller applikationen beror på i vilken ordning delar av koden exekveras. Kom ihåg vårt exempel där vi startade trådar:

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();
       }
   }
}
Föreställ dig nu att programmet ansvarar för att driva en robot som lagar mat! Tråd-0 får ut ägg ur kylen. Tråd-1 sätter på spisen. Tråd-2 får en kastrull och sätter den på spisen. Tråd-3 tänder kaminen. Tråd-4 häller olja i pannan. Tråd-5 bryter äggen och häller dem i pannan. Tråd-6 slänger äggskalen i papperskorgen. Tråd-7 tar bort de kokta äggen från brännaren. Tråd-8 lägger de kokta äggen på en tallrik. Tråd-9 diskar. Titta på resultatet av vårt program: Tråd utförd: Tråd-0 Tråd utförd: Tråd-2 Tråd utförd Tråd-1 Tråd utförd: Tråd-4 Tråd utförd: Tråd-9 Tråd utförd: Tråd-5 Tråd utförd: Tråd-8 Tråd utförd: Tråd-7 Tråd utförd: Tråd-3 Är detta en komedi rutin? :) Och allt för att vårt programs arbete beror på exekveringsordningen för trådarna. Med tanke på den minsta överträdelse av den nödvändiga sekvensen förvandlas vårt kök till ett helvete, och en galen robot förstör allt runt omkring det. Detta är också ett vanligt problem i flertrådsprogrammering. Du kommer att höra om det mer än en gång. Som avslutning på den här lektionen skulle jag vilja rekommendera en bok om multithreading. Multithreading i Java: vad det är, dess fördelar och vanliga fallgropar - 6"Java Concurrency in Practice" skrevs 2006, men har inte förlorat sin relevans. Den är tillägnad flertrådad Java-programmering — från grunderna till de vanligaste misstagen och antimönster. Om du någon gång bestämmer dig för att bli en multithreading-guru är den här boken ett måste att läsa. Vi ses på nästa lektion! :)
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION