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
'sStart()
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.

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 aSystem.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:
- Start af udførelse i hovedtråden.
- Udskriv numre fra 1 til muligvis 100.000.
- 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 enTrå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.