Langoitus viittaa käytäntöön ohjelmointiprosessien suorittamisesta samanaikaisesti sovellusten suorituskyvyn parantamiseksi. Vaikka ei ole niin yleistä työskennellä ketjujen kanssa suoraan yrityssovelluksissa, niitä käytetään koko ajan Java-kehyksissä.
Esimerkiksi kehykset, jotka käsittelevät suuren määrän tietoa, kuten Spring Batch, käyttävät ketjuja tietojen hallintaan. Säikeiden tai CPU-prosessien käsittely samanaikaisesti parantaa suorituskykyä, mikä johtaa nopeampiin ja tehokkaampiin ohjelmiin.
Hanki lähdekoodi
Hanki koodi tälle Java Challengerille. Voit suorittaa omia testejä samalla kun noudatat esimerkkejä.
Etsi ensimmäinen säikeesi: Java's main () -menetelmä
Vaikka et ole koskaan työskennellyt suoraan Java-ketjujen kanssa, olet työskennellyt epäsuorasti niiden kanssa, koska Java: n main () -menetelmä sisältää pääkierteen. Aina kun olet suorittanut main ()
-menetelmällä, olet suorittanut myös pääohjelman Lanka
.
Opiskelu Lanka
luokka on erittäin hyödyllinen ymmärtämään ketjuttamisen toimintaa Java-ohjelmissa. Voimme käyttää suoritettavaa ketjua kutsumalla currentThread (). getName ()
menetelmä, kuten tässä on esitetty:
public class MainThread {public static void main (String ... mainThread) {System.out.println (Thread.currentThread (). getName ()); }}
Tämä koodi tulostaa "main", mikä tunnistaa parhaillaan suoritettavan ketjun. Tieto kuinka tunnistaa suoritettava lanka on ensimmäinen askel lanka-käsitteiden absorboimiseksi.
Java-langan elinkaari
Kun työskentelet ketjujen kanssa, on tärkeää olla tietoinen langan tilasta. Java-ketjun elinkaari koostuu kuudesta säikeetilasta:
- Uusi: Uusi
Lanka()
on instantioitu. - Ajettava:
Lanka
onalkaa()
menetelmää on käytetty. - Juoksu:
alkaa()
menetelmä on kutsuttu ja ketju on käynnissä. - Keskeytetty: Lanka on väliaikaisesti keskeytetty, ja toinen ketju voi jatkaa sitä.
- Estetty: Lanka odottaa mahdollisuutta juosta. Tämä tapahtuu, kun yksi ketju on jo kutsunut
synkronoitu()
menetelmä ja seuraavan ketjun on odotettava, kunnes se on valmis. - Lopetettu: Viestiketju on suoritettu loppuun.
Langankäynnin tiloista on enemmän tutkittavaa ja ymmärrettävää, mutta kuvan 1 tiedot riittävät tämän Java-haasteen ratkaisemiseksi.
Samanaikainen käsittely: Lankaluokan laajentaminen
Yksinkertaisimmillaan samanaikainen käsittely tapahtuu pidentämällä a Lanka
luokassa, kuten alla on esitetty.
public class InheritingThread laajentaa säiettä {InheritingThread (String threadName) {super (threadName); } public static void main (String ... periminen) {System.out.println (Thread.currentThread (). getName () + "on käynnissä"); uusi InferitingThread ("perimälanka"). start (); } @Override public void run () {System.out.println (Thread.currentThread (). GetName () + "on käynnissä"); }}
Tässä on kaksi ketjua: MainThread
ja Periytyvä lanka
. Kun vetoamme alkaa()
menetelmä uuden kanssa perimälläThread ()
, logiikka juosta()
menetelmä suoritetaan.
Välitämme myös toisen säikeen nimen Lanka
luokan rakentaja, joten tulos on:
pää on käynnissä. inheritingThread on käynnissä.
Runnable-käyttöliittymä
Perinnön sijaan voit toteuttaa Runnable-käyttöliittymän. Syöttäminen Ajettava
sisällä a Lanka
konstruktori johtaa vähemmän kytkentään ja enemmän joustavuutta. Ohituksen jälkeen Ajettava
, voimme käyttää alkaa()
menetelmää aivan kuten edellisessä esimerkissä:
public class RunnableThread toteuttaa Runnable {public static void main (String ... runnableThread) {System.out.println (Thread.currentThread (). getName ()); uusi säie (uusi RunnableThread ()). start (); } @Override public void run () {System.out.println (Thread.currentThread (). GetName ()); }}
Ei-daemon vs daemon-säikeet
Suorituksen suhteen ketjuja on kahdenlaisia:
- Muut kuin daemon-ketjut suoritetaan loppuun asti. Pääkierre on hyvä esimerkki ei-daemon-säikeestä. Koodi sisään
main ()
suoritetaan aina loppuun asti, ellei aSystem.exit ()
pakottaa ohjelman loppuun. - A daemon-säie on päinvastoin, periaatteessa prosessi, jota ei tarvitse suorittaa loppuun asti.
Muista sääntö: Jos ympäröivä muu kuin daemon-säie päättyy ennen daemon-säiettä, daemon-säie suoritetaan vasta loppuun asti.
Tutki tätä esimerkkiä ymmärtääksesi paremmin daemon- ja non-daemon-säikeiden välisen suhteen:
tuo java.util.stream.IntStream; public class NonDaemonAndDaemonThread {public static void main (String ... nonDaemonAndDaemon) heittää InterruptedException {System.out.println ("Suorituksen aloittaminen langassa" + Thread.currentThread (). getName ()); Viestiketju daemonThread = new Thread (() -> IntStream.rangeClosed (1, 100000) .forEach (System.out :: println)); daemonThread.setDaemon (true); daemonThread.start (); Lanka. Unessa (10); System.out.println ("Suorituksen loppu säikeessä" + Thread.currentThread (). GetName ()); }}
Tässä esimerkissä olen käyttänyt daemon-säiettä ilmoittaaksesi alueen välillä 1 - 100 000, iteroiden ne kaikki ja tulosta sitten. Mutta muista, että daemon-säie ei suorita suoritusta loppuun, jos ei-daemon-päälanka päättyy ensin.
Tuotos etenee seuraavasti:
- Suorituskyvyn aloitus pääketjussa.
- Tulosta numerot 1: stä 100 000: een.
- Suorituskyvyn loppu pääketjussa, todennäköisesti ennen iterointia 100 000: een.
Lopullinen tulos riippuu JVM-toteutuksestasi.
Ja se vie minut seuraavaan kohtaan: langat ovat arvaamattomia.
Langan prioriteetti ja JVM
Langan suorittaminen on mahdollista priorisoida setPriority
menetelmä, mutta miten se hoidetaan, riippuu JVM: n toteutuksesta. Linuxilla, MacOS: lla ja Windowsilla on kaikilla erilaiset JVM-toteutukset, ja kukin käsittelee langan prioriteettia omien oletusarvojensa mukaan.
Määritetty säieprioriteetti vaikuttaa kuitenkin langan kutsun järjestykseen. Kolme vakiota, jotka on ilmoitettu Lanka
luokat ovat:
/ ** * Pienin prioriteetti, joka ketjulla voi olla. * / julkinen staattinen lopullinen int MIN_PRIORITY = 1; / ** * Ketjulle määritetty oletusprioriteetti. * / julkinen staattinen lopullinen int NORM_PRIORITY = 5; / ** * Suurin prioriteetti, joka ketjulla voi olla. * / julkinen staattinen lopullinen int MAX_PRIORITY = 10;
Kokeile suorittaa joitain testejä seuraavalla koodilla nähdäksesi, mikä toteutusprioriteetti päädyt:
public class ThreadPriority {public static void main (String ... threadPriority) {Thread moeThread = new Thread (() -> System.out.println ("Moe")); Kierre barneyThread = uusi ketju (() -> System.out.println ("Barney")); Viestiketju homerThread = new Thread (() -> System.out.println ("Homer")); moeThread.setPriority (ketju.MAX_PRIORITY); barneyThread.setPriority (Thread.NORM_PRIORITY); homerThread.setPriority (Thread.MIN_PRIORITY); homerThread.start (); barneyThread.start (); moeThread.start (); }}
Vaikka asetamme moeKierre
kuten MAX_PRIORITY
, emme voi luottaa siihen, että tämä säie suoritetaan ensin. Sen sijaan toteutusjärjestys on satunnainen.
Vakiot vs. enums
Lanka
luokka esiteltiin Java 1.0: lla. Tuolloin prioriteetit asetettiin vakioiden, ei enumien avulla. Vakioiden käytössä on kuitenkin ongelma: jos välitämme prioriteettinumeron, joka ei ole alueella 1-10, setPriority ()
menetelmä heittää IllegalArgumentExceptionin. Tänään voimme käyttää enumeja kiertääksesi tämän ongelman. Enumien käyttäminen tekee laittoman argumentin välittämisen mahdottomaksi, mikä yksinkertaistaa koodia ja antaa meille paremman hallinnan sen suorituksessa.
Vastaa Java-säikeiden haasteeseen!
Olet oppinut vain vähän säikeistä, mutta se riittää tämän viestin Java-haasteeseen.
Aloita tutkimalla seuraavaa koodia:
julkinen luokka ThreadChallenge {yksityinen staattinen int ahmaAdrenaliini = 10; public static void main (String ... doYourBest) {uusi moottoripyörä ("Harley Davidson"). start (); Moottoripyörä fastBike = uusi moottoripyörä ("Dodge Tomahawk"); fastBike.setPriority (ketju.MAX_PRIORITY); fastBike.setDaemon (väärä); fastBike.start (); Moottoripyörä yamaha = uusi moottoripyörä ("Yamaha YZF"); yamaha.setPriority (ketju.MIN_PRIORITY); yamaha.start (); } staattinen luokka Moottoripyörä jatkuu Lanka {Moottoripyörä (String bikeName) {super (bikeName); } @Override public void run () {wolverineAdrenaline ++; if (ahmaAdrenaliini == 13) {System.out.println (this.getName ()); }}}}
Mikä on tämän koodin lähtö? Analysoi koodi ja yritä selvittää vastaus itsellesi oppimiesi perusteella.
Harley Davidson
B.Dodge Tomahawk
C. Yamaha YZF
D. Määrittelemätön
Mitä juuri tapahtui? Ymmärtäminen ketjujen käyttäytymistä
Yllä olevassa koodissa loimme kolme säiettä. Ensimmäinen lanka on Harley Davidson
ja annoimme tälle säikeelle oletusprioriteetin. Toinen lanka on Dodge Tomahawk
, määritetty MAX_PRIORITY
. Kolmas on Yamaha YZF
, kanssa MIN_PRIORITY
. Sitten aloitimme langat.
Jos haluat määrittää, missä järjestyksessä ketjut toimivat, muista ensin, että Moottoripyörä
luokka laajentaa Lanka
luokka ja että olemme välittäneet ketjun nimen konstruktorissa. Olemme myös ohittaneet juosta()
menetelmä ehdolla: jos ahma Adrenaliini on yhtä suuri kuin 13
.
Vaikkakin Yamaha YZF
on kolmas säikeemme toteutusjärjestyksessä ja on MIN_PRIORITY
, ei ole takeita siitä, että se suoritetaan viimeisenä kaikille JVM-toteutuksille.
Saatat myös huomata, että tässä esimerkissä asetamme Dodge Tomahawk
lanka kuten daemon
. Koska se on daemon-säie, Dodge Tomahawk
ei koskaan saa suorittaa loppuun. Mutta kaksi muuta säiettä eivät oletusarvoisesti ole daemonit, joten Harley Davidson
ja Yamaha YZF
ketjut täydentävät ehdottomasti niiden suorittamisen.
Lopuksi, tulos on D: Määrittelemätön, koska ei ole takeita siitä, että säikeiden ajoittaja noudattaa suoritusjärjestystä tai langan prioriteettia.
Muista, ettemme voi luottaa ohjelmalogiikkaan (ketjujen järjestys tai ketjuprioriteetti) JVM: n suorittamisjärjestyksen ennustamiseen.
Video-haaste! Muuttujien argumenttien virheenkorjaus
Virheenkorjaus on yksi helpoimmista tavoista hyödyntää ohjelmointikonsepteja täysin ja parantaa samalla koodiasi. Tässä videossa voit seurata mukana, kun minä virheenkorjaan ja selitän ketjun käyttäytymishaastetta:
Java-ketjujen yleiset virheet
- Kutsuminen
juosta()
menetelmä yrittää aloittaa uusi ketju. - Yritetään aloittaa ketju kahdesti (tämä aiheuttaa
IllegalThreadStateException
). - Sallitaan useiden prosessien muuttaa kohteen tilaa, kun sen ei pitäisi muuttua.
- Kirjoitetaan ohjelmalogiikkaa, joka perustuu ketjun prioriteettiin (et voi ennustaa sitä).
- Luotu ketjun suoritusjärjestykseen - vaikka aloitamme langan ensin, ei ole mitään takeita siitä, että se suoritetaan ensin.
Mitä kannattaa muistaa Java-ketjuista
- Kutsu
alkaa()
menetelmä aloittaa aLanka
. - On mahdollista pidentää
Lanka
luokassa, jotta säikeitä voidaan käyttää. - On mahdollista toteuttaa ketjutoiminto a: n sisällä
Ajettava
käyttöliittymä. - Langan prioriteetti riippuu JVM: n toteutuksesta.
- Langankäyttäytyminen riippuu aina JVM: n toteutuksesta.
- Daemon-säie ei ole valmis, jos sulkeutuva muu kuin daemon-säie päättyy ensin.
Lisätietoja Java-säikeistä JavaWorldissa
- Lue Java 101 -sarjan sarjat, jos haluat lisätietoja ketjuista ja ajettavista ohjelmista, ketjujen synkronoinnista, ketjujen ajoituksesta odotuksella / ilmoituksella ja langan kuolemasta.
- Moderni ketjutus: Java-samanaikaisuusalusta esittelee
java.util.concurrent
ja vastaa yleisiin kysymyksiin kehittäjille, jotka ovat aloittaneet Java-samanaikaisuuden. - Moderni ketjuttaminen ei aivan aloittelijoille tarjoaa edistyneempiä vinkkejä ja parhaita käytäntöjä työskentelyyn
java.util.concurrent
.
Lisää Rafaelilta
- Hanki lisää pikavinkivinkkejä: Lue kaikki Java Challengers -sarjan viestit.
- Rakenna Java-taitosi: Käy Java Dev Gym -koodiharjoituksessa.
- Haluatko työskennellä stressittömissä projekteissa ja kirjoittaa virheetöntä koodia? Käy NoBugsProjectissa kopiosi Ei vikoja, ei stressiä - luo elämää muuttava ohjelmisto tuhoamatta elämääsi.
Tämän tarinan "Langankäyttäytyminen JVM: ssä" julkaisi alun perin JavaWorld.