Ohjelmointi

Langankäyttäytyminen JVM: ssä

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: Lankaon alkaa() 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.
Rafael Chinelato Del Nero

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 a System.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:

  1. Suorituskyvyn aloitus pääketjussa.
  2. Tulosta numerot 1: stä 100 000: een.
  3. 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 Davidsonja 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 a Lanka.
  • 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.

$config[zx-auto] not found$config[zx-overlay] not found