Samanaikaisuus Javassa

Java - ohjelmointikieli ja JVM ( Java Virtual Machine ) on suunniteltu tukemaan rinnakkaislaskentaa , ja kaikki laskennat suoritetaan säikeen yhteydessä . Useat säikeet voivat jakaa objekteja ja resursseja; jokainen säie suorittaa omat käskynsä (koodinsa), mutta voi mahdollisesti käyttää mitä tahansa ohjelman objektia. Ohjelmoijan vastuulla on koordinoida (tai " synkronoida ") säikeitä luku- ja kirjoitustoimintojen aikana jaetuissa objekteissa. Säikeen synkronointi tarvitaan sen varmistamiseksi, että vain yksi säie voi käyttää objektia kerrallaan ja estää säiettä pääsemästä epätäydellisesti päivitettyihin objekteihin, kun toinen säie työskentelee niiden parissa. Java-kielessä on sisäänrakennetut säietynkronoinnin tukirakenteet.

Prosessit ja säikeet

Useimmat Java-virtuaalikoneen toteutukset käyttävät yhtä prosessia ohjelman suorittamiseen, ja Java-ohjelmointikielessä rinnakkaislaskenta yhdistetään yleisimmin säikeisiin . Säikeitä kutsutaan joskus valoprosesseiksi .

Suoratoista objekteja

Säikeet jakavat keskenään prosessiresursseja, kuten muistia ja avoimia tiedostoja. Tämä lähestymistapa johtaa tehokkaaseen, mutta mahdollisesti ongelmalliseen viestintään. Jokaisessa sovelluksessa on vähintään yksi käynnissä oleva säie. Säiettä, josta ohjelman suoritus alkaa, kutsutaan main tai main . Pääsäie pystyy luomaan lisäsäikeitä objektien Runnabletai Callable. (Liitäntä Callableon samanlainen siinä mielessä, Runnableettä molemmat on suunniteltu luokille, jotka instantoidaan erillisessä säikeessä. Runnable, ei kuitenkaan palauta tulosta eikä voi heittää tarkistettua poikkeusta .)

Jokainen säie voidaan ajastaa toimimaan erillisessä suoritinytimessä, käyttämään aikaleikkausta yhdessä prosessoriytimessä tai käyttämään aikaleikkausta useissa prosessoreissa. Kahdessa viimeisessä tapauksessa järjestelmä vaihtaa ajoittain säikeiden välillä sallien vuorotellen yhden tai toisen säikeen suorittaa. Tätä kaavaa kutsutaan pseudoparallelismiksi. Ei ole olemassa universaalia ratkaisua, joka kertoisi tarkalleen, kuinka Java-säikeet muunnetaan käyttöjärjestelmän alkuperäisiksi säikeiksi. Se riippuu tietystä JVM-toteutuksesta.

Javassa säiettä esitetään aliobjektina Thread. Tämä luokka sisältää standardikierteitysmekanismit. Säikeitä voidaan hallita joko suoraan tai abstraktien mekanismien, kuten Executorin ja java.util.concurrent-paketin kokoelmien avulla.

Viestiketjun ajaminen

On kaksi tapaa aloittaa uusi viestiketju:

  • Runnable-käyttöliittymän käyttöönotto
public class HelloRunnable toteuttaa Runnable { public void run () { System . ulos . println ( "Terve ketjusta!" ); } public static void main ( String [] args ) { ( new Thread ( uusi HelloRunnable ())). aloita (); } }
  • Periytys säikeen luokasta
public class HelloThread laajentaa säiettä { public void run () { System . ulos . println ( "Terve ketjusta!" ); } public static void main ( String [] args ) { ( uusi HelloThread ()). aloita (); } } Keskeyttää

Keskeytys on merkki säikeelle, että sen pitäisi pysäyttää nykyinen työ ja tehdä jotain muuta. Säie voi lähettää keskeytyksen kutsumalla objektin interrupt()Thread -menetelmää, jos sen on keskeytettävä siihen liittyvä säie. Keskeytysmekanismi toteutetaan käyttämällä luokan sisäistä keskeytystilaa (keskeytyslippu) Thread. Thread.interrupt ():n kutsuminen nostaa tämän lipun. Sopimuksen mukaan mikä tahansa menetelmä, joka päättyy InterruptedExceptioniin , nollaa keskeytyslipun. On kaksi tapaa tarkistaa, onko tämä lippu asetettu. Ensimmäinen tapa on kutsua säieobjektin bool isInterrupted() -metodia , toinen tapa on kutsua staattista boolia Thread.interrupted () -metodi . Ensimmäinen menetelmä palauttaa keskeytyslipun tilan ja jättää tämän lipun koskemattomaksi. Toinen menetelmä palauttaa lipun tilan ja nollaa sen. Huomaa, että Thread.interrupted()  on luokan staattinen menetelmä Thread, ja sen kutsuminen palauttaa sen säikeen keskeytyslipun arvon, josta se on kutsuttu.

Odotetaan valmistumista

Java tarjoaa mekanismin, joka sallii yhden säikeen odottaa toisen säikeen suorittamisen loppuun. Tätä varten käytetään Thread.join() -metodia .

Demonit

Javassa prosessi päättyy, kun sen viimeinen säie päättyy. Vaikka main()-menetelmä olisi jo valmis, mutta sen synnyttämät säikeet ovat edelleen käynnissä, järjestelmä odottaa niiden valmistumista. Tämä sääntö ei kuitenkaan koske erityistä säiettä - demoneja. Jos prosessin viimeinen normaali säie on päättynyt ja vain demonsäikeitä on jäljellä, ne lopetetaan väkisin ja prosessi päättyy. Useimmiten daemon-säikeitä käytetään suorittamaan taustatehtäviä, jotka palvelevat prosessia sen elinkaaren aikana.

Säikeen julistaminen daemoniksi on melko yksinkertaista - sinun täytyy kutsua sen setDaemon(true) -metodia ennen säikeen aloittamista ; Voit tarkistaa, onko säiettä demoni kutsumalla sen loogista isDaemon()- metodia .

Poikkeukset

Heitetty ja käsittelemätön poikkeus aiheuttaa ketjun päättymisen. Pääsäie tulostaa poikkeuksen automaattisesti konsoliin, ja käyttäjien luomat säikeet voivat tehdä sen vain rekisteröimällä käsittelijän. [1] [2]

Muistimalli

Java-muistimalli [1] kuvaa säikeiden vuorovaikutusta muistin läpi Java-ohjelmointikielessä. Usein nykyaikaisissa tietokoneissa koodia ei suoriteta siinä järjestyksessä, jossa se on kirjoitettu nopeuden vuoksi. Permutoinnin tekevät kääntäjä , prosessori ja muistialijärjestelmä . Java-ohjelmointikieli ei takaa operaatioiden atomiteettia ja peräkkäistä johdonmukaisuutta luettaessa tai kirjoitettaessa jaettujen objektien kenttiä. Tämä ratkaisu vapauttaa kääntäjän kädet ja mahdollistaa optimoinnin (kuten rekisterin allokoinnin , yleisten alilausekkeiden poistamisen ja redundanttien lukutoimintojen eliminoinnin ), jotka perustuvat muistin käyttötoimintojen permutaatioon. [3]

Synkronointi

Säikeet kommunikoivat jakamalla pääsyn kenttiin ja kenttien viittaamiin objekteihin. Tämä viestintämuoto on erittäin tehokas, mutta se mahdollistaa kahdenlaisia ​​virheitä: säikeen häiriöt ja muistin johdonmukaisuusvirheet. Niiden esiintymisen estämiseksi on olemassa synkronointimekanismi.

Uudelleenjärjestäminen (uudelleenjärjestäminen, uudelleenjärjestäminen) ilmenee virheellisesti synkronoiduissa monisäikeisissä ohjelmissa, joissa yksi säie saattaa havaita muiden säikeiden tuottamia vaikutuksia ja tällaiset ohjelmat voivat havaita muuttujien päivitetyn arvojen näkyvän muille säikeille eri säikeissä. järjestys kuin lähdekoodissa on määritetty.

Säikeiden synkronointiin Javassa käytetään monitoreja , jotka ovat korkean tason mekanismi, joka sallii vain yhden säikeen kerrallaan suorittaa näytöllä suojatun koodilohkon. Monitorien käyttäytymistä tarkastellaan lukkojen kannalta ; Jokaiseen objektiin on liitetty yksi lukko.

Synkronointiin liittyy useita näkökohtia. Parhaiten ymmärretty on molemminpuolinen poissulkeminen - vain yksi säie voi omistaa näytön, joten synkronointi näytöllä tarkoittaa, että kun yksi säie tulee monitorin suojaamaan synkronoituun lohkoon, mikään muu säie ei pääse tämän näytön suojaamaan lohkoon ennen ensimmäistä säiettä poistuu synkronoidusta lohkosta.

Mutta synkronointi on enemmän kuin vain molemminpuolinen poissulkeminen. Synkronointi varmistaa, että muistiin ennen synkronoitua lohkoa tai sen sisällä kirjoitetut tiedot näkyvät muille samassa näytössä synkronoiduille säikeille. Synkronoidusta lohkosta poistuttuamme vapautamme monitorin, mikä huuhtelee välimuistin päämuistiin, jotta säikeemme tekemät kirjoitukset voivat näkyä muille säikeille. Ennen kuin voimme siirtyä synkronoituun lohkoon, hankimme monitorin, mikä mitätöi paikallisen prosessorin välimuistin, jolloin muuttujat ladataan päämuistista. Sitten voimme nähdä kaikki merkinnät, jotka näkyvät näytön edellisessä versiossa. (JSR 133)

Kentän luku-kirjoitus on atomioperaatio, jos kenttä on joko julistettu haihtuvaksi tai suojattu ainutlaatuisella lukituksella , joka on hankittu ennen luku-kirjoitusta.

Lukot ja synkronoidut lohkot

Keskinäisen poissulkemisen ja säietynkronoinnin vaikutus saavutetaan syöttämällä synkronoitu lohko tai menetelmä, joka hankkii lukon implisiittisesti, tai hankkimalla lukon eksplisiittisesti (kuten ReentrantLockjava.util.concurrent.locks-paketista). Molemmilla lähestymistavoilla on sama vaikutus muistin käyttäytymiseen. Jos kaikki pääsyyritykset tiettyyn kenttään on suojattu samalla lukituksella, tämän kentän luku-kirjoitustoiminnot ovat atomisia .

Haihtuvat kentät

Kun avainsanaa käytetään kenttiin, se volatiletakaa:

  1. (Kaikissa Java-versioissa) -muuttujan volatilekäyttöoikeudet järjestetään globaalisti. Tämä tarkoittaa, että jokainen -kenttään käyttävä säievolatile lukee arvon ennen jatkamista sen sijaan, että (jos mahdollista) käyttäisi välimuistissa olevaa arvoa. (Pääsyjä volatile-muuttujiin ei voi järjestää uudelleen toistensa kanssa, mutta ne voidaan järjestää uudelleen käyttämällä tavallisia muuttujia. Tämä tekee tyhjäksi -kenttien hyödyllisyyden volatileviestinnän keinona säikeestä toiseen.)
  2. (Java 5:ssä ja uudemmissa versioissa) -kenttään kirjoittamisella on volatilesama vaikutus muistiin kuin näytön julkaisuun , kun taas lukemisella on sama vaikutus kuin monitorin hankinnalla .  Pääsy -kenttään muodostaa " tapahtuu ennen " -suhteen . [4] Pohjimmiltaan tämä relaatio on tae siitä, että kaikki, mikä oli näkyvissä säikeelle, kun se kirjoitti -kenttään , tulee näkyviin säikeelle, kun se lukee . volatile AvolatilefBf

Volatile-kentät ovat atomisia. -kentästä lukemisella volatileon sama vaikutus kuin lukon hankkimisella: työmuistin tiedot julistetaan kelpaamattomiksi ja volatile-kentän arvo luetaan uudelleen muistista. -kenttään kirjoittamisella volatileon sama vaikutus muistiin kuin lukon vapauttamisella: volatile-kenttä kirjoitetaan välittömästi muistiin.

Viimeiset kentät

Lopulliseksi julistettua kenttää kutsutaan lopulliseksi, eikä sitä voi muuttaa alustuksen jälkeen. Objektin lopulliset kentät alustetaan sen rakentajassa. Jos rakentaja noudattaa tiettyjä yksinkertaisia ​​sääntöjä, lopullisen kentän oikea arvo näkyy muille säikeille ilman synkronointia. Yksinkertainen sääntö on, että tämä viittaus ei saa jättää rakentajaa ennen kuin se on valmis.

Historia

JDK 1.2 : sta alkaen Java sisältää vakiosarjan Java Collections Framework - kokoelmaluokkia .

Doug Lee , joka osallistui myös Java Collections Frameworkin toteuttamiseen, kehitti samanaikaisuuspaketin, joka sisältää useita synkronointiprimitiivejä ja suuren määrän kokoelmaan liittyviä luokkia. [5] Työtä sen parissa jatkettiin osana JSR 166 :ta [6] Doug Leen johdolla .

JDK 5.0 -julkaisu sisälsi monia lisäyksiä ja selvennyksiä Java-concurrency-malliin. Ensimmäistä kertaa JSR 166:n kehittämät samanaikaisuuden API:t sisällytettiin JDK:han. JSR 133 tarjosi tuen hyvin määriteltyihin atomitoimintoihin monisäikeisessä/moniprosessoriympäristössä.

Sekä Java SE 6 että Java SE 7 tuovat muutoksia ja lisäyksiä JSR 166 API:hen.

Katso myös

Muistiinpanot

  1. Oracle Interface Thread.UncaughtExceptionHandler . Haettu 10. toukokuuta 2014. Arkistoitu alkuperäisestä 12. toukokuuta 2014.
  2. Silent Thread -kuolema käsittelemättömistä poikkeuksista . readjava.com . Haettu 10. toukokuuta 2014. Arkistoitu alkuperäisestä 12. toukokuuta 2014.
  3. Herlihy, Maurice ja Nir Shavit. "Moniprosessoriohjelmoinnin taito." PODC. Voi. 6. 2006.
  4. Osa 17.4.4: Synkronointijärjestys Java® Language Specification, Java SE 7 Edition . Oracle Corporation (2013). Haettu 12. toukokuuta 2013. Arkistoitu alkuperäisestä 3. helmikuuta 2021.
  5. Doug Lee . Yleiskatsaus paketista util.concurrent Release 1.3.4 . — « Huomautus: J2SE 5.0:n julkaisun jälkeen tämä paketti siirtyy ylläpitotilaan: Vain olennaiset korjaukset vapautetaan. J2SE5-paketti java.util.concurrent sisältää parannettuja, tehokkaampia, standardoituja versioita tämän paketin pääkomponenteista. ". Haettu 1. tammikuuta 2011. Arkistoitu alkuperäisestä 18. joulukuuta 2020.
  6. JSR 166: Concurrency Utilities (linkki ei saatavilla) . Haettu 3. marraskuuta 2015. Arkistoitu alkuperäisestä 3. marraskuuta 2016. 

Linkit

  • Goetz, Brian; Joshua Bloch; Joseph Bowbeer; Doug Lea; David Holmes Tim Peierls. Java Concurrency in Practice  (uuspr.) . - Addison Wesley , 2006. - ISBN 0-321-34960-1 .
  • Leah, Doug. Rinnakkaisohjelmointi Javassa : Suunnitteluperiaatteet ja -mallit  . - Addison Wesley , 1999. - ISBN 0-201-31009-0 .

Linkkejä ulkoisiin resursseihin