Programmering

Trådadfærd i JVM

Trådning henviser til praksis med at udføre programmeringsprocesser samtidigt for at forbedre applikationsydelsen. Selvom det ikke er så almindeligt at arbejde med tråde direkte i forretningsapplikationer, bruges de hele tiden i Java-rammer.

Som et eksempel bruger rammer, der behandler en stor mængde information, som Spring Batch, tråde til at administrere data. Manipulering af tråde eller CPU-processer forbedrer samtidigt ydeevnen, hvilket resulterer i hurtigere og mere effektive programmer.

Få kildekoden

Få koden til denne Java Challenger. Du kan køre dine egne tests, mens du følger eksemplerne.

Find din første tråd: Java's vigtigste () metode

Selvom du aldrig har arbejdet direkte med Java-tråde, har du arbejdet indirekte med dem, fordi Java's main () -metode indeholder en hovedtråd. Hver gang du har henrettet hoved () metode, har du også udført main Tråd.

Studerer Tråd klasse er meget nyttig til at forstå, hvordan threading fungerer i Java-programmer. Vi kan få adgang til den tråd, der udføres ved at påkalde currentThread (). getName () metode, som vist her:

 public class MainThread {public static void main (String ... mainThread) {System.out.println (Thread.currentThread (). getName ()); }} 

Denne kode udskrives "main", der identificerer den tråd, der aktuelt udføres. At vide, hvordan man identificerer den tråd, der udføres, er det første skridt til at absorbere trådkoncepter.

Java-trådens livscyklus

Når du arbejder med tråde, er det vigtigt at være opmærksom på trådtilstand. Java-trådens livscyklus består af seks trådtilstande:

  • Ny: Et nyt Tråd() er instantieret.
  • Kan køres: Det Tråd's Start() metode er påberåbt.
  • Løb: Det Start() metoden er blevet påberåbt, og tråden kører.
  • Suspenderet: Tråden er midlertidigt suspenderet og kan genoptages af en anden tråd.
  • Blokeret: Tråden venter på en mulighed for at løbe. Dette sker, når en tråd allerede har påberåbt sig synkroniseret () metode og den næste tråd skal vente, indtil den er færdig.
  • Afsluttet: Trådens udførelse er fuldført.
Rafael Chinelato Del Nero

Der er mere at udforske og forstå om trådtilstande, men oplysningerne i figur 1 er nok til at du kan løse denne Java-udfordring.

Samtidig behandling: Udvidelse af en trådklasse

Når det er enklest, sker samtidig behandling ved at udvide en Tråd klasse, som vist nedenfor.

 offentlig klasse InheritingThread udvider tråd {InheritingThread (String threadName) {super (threadName); } offentlig statisk ugyldig hoved (String ... inheriting) {System.out.println (Thread.currentThread (). getName () + "kører"); ny InheritingThread ("inheritingThread"). start (); } @ Override public void run () {System.out.println (Thread.currentThread (). GetName () + "kører"); }} 

Her kører vi to tråde: Hovedtråd og ArvTråd. Når vi påberåber os Start() metode med den nye inheritingThread (), logikken i løb() metode udføres.

Vi sender også navnet på den anden tråd i Tråd klassekonstruktør, så output vil være:

 main kører. inheritingThread kører. 

Den kørbare grænseflade

I stedet for at bruge arv, kan du implementere Runnable-grænsefladen. Aflevering Kan køres inde i en Tråd konstruktør resulterer i mindre kobling og mere fleksibilitet. Efter at have passeret Kan køres, kan vi påberåbe sig Start() metode nøjagtigt som vi gjorde i det foregående eksempel:

 offentlig klasse RunnableThread implementerer Runnable {public static void main (String ... runnableThread) {System.out.println (Thread.currentThread (). getName ()); ny tråd (ny RunnableThread ()). start (); } @ Override public void run () {System.out.println (Thread.currentThread (). GetName ()); }} 

Ikke-dæmon vs dæmon tråde

Med hensyn til udførelse er der to typer tråde:

  • Tråde, der ikke er dæmon udføres indtil slutningen. Hovedtråden er et godt eksempel på en tråd, der ikke er dæmon. Kode i hoved () vil altid blive udført indtil slutningen, medmindre a System.exit () tvinger programmet til at gennemføre.
  • EN dæmon tråd er det modsatte, dybest set en proces, der ikke skal udføres indtil slutningen.

Husk reglen: Hvis en omslutningstråd, der ikke er dæmon, ender før en dæmontråd, udføres dæmontråden først indtil slutningen.

For bedre at forstå forholdet mellem dæmon og ikke-dæmon tråde, studer dette eksempel:

 importere java.util.stream.IntStream; public class NonDaemonAndDaemonThread {public static void main (String ... nonDaemonAndDaemon) kaster InterruptedException {System.out.println ("Starter udførelsen i tråden" + Thread.currentThread (). getName ()); Tråd-dæmonTråd = ny tråd (() -> IntStream.rangeClosed (1, 100000). ForEach (System.out :: println)); daemonThread.setDaemon (sand); daemonThread.start (); Tråd. Søvn (10); System.out.println ("Afslutning af udførelsen i tråden" + Thread.currentThread (). GetName ()); }} 

I dette eksempel har jeg brugt en dæmontråd til at erklære et interval fra 1 til 100.000, gentage dem alle og derefter udskrive. Men husk, en dæmontråd vil ikke fuldføre udførelsen, hvis ikke-dæmonens hovedtråd slutter først.

Outputtet fortsætter som følger:

  1. Start af udførelse i hovedtråden.
  2. Udskriv numre fra 1 til muligvis 100.000.
  3. Afslutning af udførelse i hovedtråden, sandsynligvis inden iteration til 100.000 er afsluttet.

Den endelige output afhænger af din JVM-implementering.

Og det bringer mig til mit næste punkt: tråde er uforudsigelige.

Trådprioritet og JVM

Det er muligt at prioritere trådudførelse med sætPrioritet metode, men hvordan det håndteres, afhænger af JVM-implementeringen. Linux, MacOS og Windows har alle forskellige JVM-implementeringer, og hver håndterer trådprioritet i henhold til sine egne standardindstillinger.

Den trådprioritet, du indstiller, påvirker dog rækkefølgen af ​​trådopkald. De tre konstanter, der er erklæret i Tråd klasse er:

 / ** * Den mindste prioritet, som en tråd kan have. * / offentlig statisk endelig int MIN_PRIORITY = 1; / ** * Standardprioriteten, der er tildelt en tråd. * / offentlig statisk endelig int NORM_PRIORITY = 5; / ** * Den maksimale prioritet, som en tråd kan have. * / offentlig statisk endelig int MAX_PRIORITY = 10; 

Prøv at køre nogle tests på følgende kode for at se, hvilken udførelsesprioritet du ender med:

 public class ThreadPriority {public static void main (String ... threadPriority) {Thread moeThread = new Thread (() -> System.out.println ("Moe")); Tråd barneyThread = ny tråd (() -> System.out.println ("Barney")); Tråd homerThread = ny tråd (() -> System.out.println ("Homer")); moeThread.setPriority (Thread.MAX_PRIORITY); barneyThread.setPriority (Thread.NORM_PRIORITY); homerThread.setPriority (Thread.MIN_PRIORITY); homerThread.start (); barneyThread.start (); moeThread.start (); }} 

Selv hvis vi sætter os moeTråd som MAX_PRIORITY, vi kan ikke stole på, at denne tråd udføres først. I stedet for vil rækkefølgen være tilfældig.

Konstanter vs enums

Det Tråd klasse blev introduceret med Java 1.0. På det tidspunkt blev prioriteter sat ved hjælp af konstanter, ikke enums. Der er et problem med at bruge konstanter, men hvis vi passerer et prioritetsnummer, der ikke er i området 1 til 10, setPriority () metoden vil kaste en IllegalArgumentException. I dag kan vi bruge enums til at omgå dette problem. Brug af enums gør det umuligt at føre et ulovligt argument, som både forenkler koden og giver os mere kontrol over dens udførelse.

Tag udfordringen med Java-tråde!

Du har lært lidt om tråde, men det er nok til dette indlægs Java-udfordring.

For at starte skal du studere følgende kode:

 offentlig klasse ThreadChallenge {privat statisk int wolverineAdrenaline = 10; public static void main (String ... doYourBest) {new Motorcycle ("Harley Davidson"). start (); Motorcykel fastBike = ny motorcykel ("Dodge Tomahawk"); fastBike.setPriority (Thread.MAX_PRIORITY); fastBike.setDaemon (falsk); fastBike.start (); Motorcykel Yamaha = ny motorcykel ("Yamaha YZF"); yamaha.setPriority (tråd.MIN_PRIORITY); yamaha.start (); } statisk klasse Motorcykel udvider Tråd {Motorcykel (String bikeName) {super (bikeName); } @ Override public void run () {wolverineAdrenaline ++; hvis (wolverineAdrenaline == 13) {System.out.println (this.getName ()); }}}} 

Hvad bliver output af denne kode? Analyser koden, og prøv at bestemme svaret selv, baseret på hvad du har lært.

A. Harley Davidson

B. Dodge Tomahawk

C. Yamaha YZF

D. Ubestemt

Hvad skete der lige? Forståelse af tråde adfærd

I ovenstående kode oprettede vi tre tråde. Den første tråd er Harley Davidson, og vi tildelte denne tråd standardprioriteten. Den anden tråd er Dodge Tomahawk, tildelt MAX_PRIORITY. Den tredje er Yamaha YZF, med MIN_PRIORITY. Så startede vi trådene.

For at bestemme rækkefølgen, som trådene kører i, kan du først bemærke, at Motorcykel klasse udvider Tråd klasse, og at vi har bestået trådnavnet i konstruktøren. Vi har også tilsidesat løb() metode med en betingelse: hvis jerv Adrenalin er lig med 13.

Selv om Yamaha YZF er den tredje tråd i vores rækkefølge og har MIN_PRIORITY, der er ingen garanti for, at den udføres sidst for alle JVM-implementeringer.

Du bemærker muligvis også, at vi i dette eksempel indstiller Dodge Tomahawk tråd som dæmon. Fordi det er en dæmontråd, Dodge Tomahawk muligvis aldrig fuldføre udførelsen. Men de to andre tråde er ikke-dæmoner som standard, så den Harley Davidson og Yamaha YZF tråde vil helt sikkert afslutte deres udførelse.

Afslutningsvis bliver resultatet D: Ubestemt, fordi der ikke er nogen garanti for, at trådplanlæggeren følger vores rækkefølge for udførelse eller trådprioritet.

Husk, vi kan ikke stole på programlogik (rækkefølge af tråde eller trådprioritet) for at forudsige JVM's rækkefølge for udførelse.

Video udfordring! Fejlretning af variable argumenter

Fejlfinding er en af ​​de nemmeste måder til fuldt ud at absorbere programmeringskoncepter, samtidig med at du forbedrer din kode. I denne video kan du følge med, mens jeg fejler og forklarer udfordringen om trådadfærd:

Almindelige fejl med Java-tråde

  • Påberåbe sig løb() metode til at prøve at starte en ny tråd.
  • Forsøger at starte en tråd to gange (dette vil medføre en IllegalThreadStateException).
  • Tillader flere processer at ændre et objekts tilstand, når det ikke skal ændres.
  • Skrive programlogik, der er afhængig af trådprioritet (du kan ikke forudsige det).
  • Stolende på rækkefølgen af ​​trådudførelse - selvom vi starter en tråd først, er der ingen garanti for, at den udføres først.

Hvad skal man huske om Java-tråde

  • Påkald Start() metode til at starte en Tråd.
  • Det er muligt at udvide Tråd klasse direkte for at bruge tråde.
  • Det er muligt at implementere en trådhandling inde i en Kan køres interface.
  • Trådprioritet afhænger af JVM-implementeringen.
  • Trådadfærd afhænger altid af JVM-implementeringen.
  • En dæmontråd afsluttes ikke, hvis en vedlagte ikke-dæmontråd slutter først.

Lær mere om Java-tråde på JavaWorld

  • Læs Java 101-trådserien for at lære mere om tråde og kørbare, trådsynkronisering, trådplanlægning med vent / underret og tråddød.
  • Modern threading: En Java concurrency primer introducerer java.util.concurrent og svarer på almindelige spørgsmål til udviklere, der er nye i Java-samtidighed.
  • Moderne trådning til ikke helt begyndere tilbyder mere avancerede tip og bedste praksis til at arbejde med java.util.concurrent.

Mere fra Rafael

  • Få flere hurtige kodetip: Læs alle indlæg i Java Challengers-serien.
  • Opbyg dine Java-færdigheder: Besøg Java Dev Gym for en kode-træning.
  • Vil du arbejde på stressfrie projekter og skrive fejlfri kode? Besøg NoBugsProject for din kopi af Ingen fejl, ingen stress - Opret en livsændrende software uden at ødelægge dit liv.

Denne historie, "Trådadfærd i JVM" blev oprindeligt udgivet af JavaWorld.