Programiranje

Java 101: Razumevanje niti Java, 3. del: Načrtovanje niti in čakanje / obveščanje

Ta mesec nadaljujem svoj štiridelni uvod v niti Java s poudarkom na razporejanju niti, mehanizmu čakanja / obveščanja in prekinitvi niti. Raziskali boste, kako bodisi JVM bodisi načrtovalnik niti operacijskega sistema izbere naslednjo nit za izvedbo. Kot boste odkrili, je prednost izbire načrtovalca niti pomembna. Preučili boste, kako nit čaka, dokler ne prejme obvestila druge niti, preden nadaljuje z izvajanjem, in se naučili, kako uporabljati mehanizem čakanja / obveščanja za usklajevanje izvajanja dveh niti v razmerju med proizvajalcem in potrošnikom. Na koncu se boste naučili, kako prezgodaj prebuditi bodisi spečo bodisi čakalno nit za prekinitev niti ali druge naloge. Naučil vas bom tudi, kako nit, ki ne spi, ne čaka, zazna zahtevo za prekinitev iz druge niti.

Upoštevajte, da je bil ta članek (del arhiva JavaWorld) maja 2013 posodobljen z novimi seznami kod in naložljivo izvorno kodo.

Razumevanje niti Java - preberite celotno serijo

  • 1. del: Predstavljanje niti in izvedljivih datotek
  • 2. del: Sinhronizacija
  • 3. del: Načrtovanje niti, čakanje / obveščanje in prekinitev niti
  • 4. del: Skupine niti, volatilnost, lokalne spremenljivke niti, časovniki in smrt niti

Načrtovanje niti

V idealiziranem svetu bi vse programske niti imele lastne procesorje, na katerih bi lahko delovale. Dokler ne pride čas, ko imajo računalniki na tisoče ali milijone procesorjev, morajo niti pogosto deliti enega ali več procesorjev. JVM ali operacijski sistem osnovne platforme dešifrira, kako deliti procesorski vir med nitmi - naloga, znana kot razporejanje niti. Tisti del JVM ali operacijskega sistema, ki izvaja razporejanje niti, je načrtovalec niti.

Opomba: Za poenostavitev razprave o načrtovanju niti se osredotočam na razporejanje niti v okviru enega procesorja. To razpravo lahko ekstrapolirate na več procesorjev; To nalogo prepuščam vam.

Zapomnite si dve pomembni točki glede razporejanja niti:

  1. Java ne vsiljuje VM, da na poseben način razporeja niti ali vsebuje načrtovalca niti. To pomeni razporejanje niti, odvisno od platforme. Zato morate biti previdni pri pisanju programa Java, katerega vedenje je odvisno od razporeditve niti in mora dosledno delovati na različnih platformah.
  2. Na srečo morate pri pisanju programov Java razmišljati o tem, kako Java razporeja niti samo takrat, ko vsaj ena od niti vašega programa dolgo uporablja procesor in se vmesni rezultati izvajanja te niti izkažejo za pomembne. Na primer, programček vsebuje nit, ki dinamično ustvari sliko. Občasno želite, da nit za risanje nariše trenutno vsebino te slike, tako da lahko uporabnik vidi, kako slika napreduje. Če želite zagotoviti, da nit izračuna ne bo monopolizirala procesorja, razmislite o razporejanju niti.

Preglejte program, ki ustvari dve procesorsko intenzivni niti:

Seznam 1. SchedDemo.java

// razred SchedDemo.java SchedDemo {public static void main (String [] args) {new CalcThread ("CalcThread A"). Start (); nov CalcThread ("CalcThread B"). start (); }} class CalcThread razširja Thread {CalcThread (ime niza) {// Posreduj ime plasti Thread. super (ime); } dvojno calcPI () {logično negativno = resnično; dvojni pi = 0,0; za (int i = 3; i <100000; i + = 2) {če (negativno) pi - = (1,0 / i); sicer pi + = (1,0 / i); negativno =! negativno; } pi + = 1,0; pi * = 4,0; vrnitev pi; } public void run () {for (int i = 0; i <5; i ++) System.out.println (getName () + ":" + calcPI ()); }}

SchedDemo ustvari dve niti, ki izračunata vrednost pi (petkrat) in natisneta vsak rezultat. Glede na to, kako vaša izvedba JVM načrtuje niti, boste morda videli izhod, podoben naslednjim:

CalcThread A: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894

V skladu z zgornjim izhodom si načrtovalec niti deli procesor med obema niti. Vendar bi lahko videli izhod, podoben temu:

CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread A: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894 CalcThread B: 3,1415726535897894

Zgornji izhod prikazuje razporejevalnik niti, ki daje prednost eni niti drugi. Zgornja izhoda ponazarjata dve splošni kategoriji načrtovalcev niti: zelena in izvorna. Njihove vedenjske razlike bom raziskal v naslednjih poglavjih. Med razpravo o vsaki kategoriji se sklicujem na stanja niti, od tega so štirje:

  1. Začetno stanje: Program je ustvaril predmet niti niti, vendar nit še ne obstaja, ker objekt niti začetek () metoda še ni bila poklicana.
  2. Tekaško stanje: To je privzeto stanje niti. Po klicu na začetek () dokonča, nit postane izvedljiva, ne glede na to, ali se ta nit izvaja, to je s pomočjo procesorja. Čeprav je veliko niti mogoče zagnati, se trenutno izvaja le ena. Načrtovalniki niti določajo, katero nit, ki jo je mogoče izvajati, dodeliti procesorju.
  3. Blokirano stanje: Ko nit izvede datoteko spanje (), počakaj (), ali pridruži se () metode, ko nit skuša prebrati podatke, ki še niso na voljo iz omrežja, in ko nit čaka, da pridobi ključavnico, je ta nit v blokiranem stanju: niti se ne izvaja niti ne more teči. (Verjetno si lahko omislite druge trenutke, ko bi nit čakal, da se nekaj zgodi.) Ko se blokirana nit odblokira, se ta nit premakne v stanje, ki ga je mogoče izvesti.
  4. Končno stanje: Ko izvedba zapusti nit teči () metoda je ta nit v zaključnem stanju. Z drugimi besedami, nit preneha obstajati.

Kako načrtovalnik niti izbere, katero nit, ki jo je mogoče izvajati, zagnati? Na to vprašanje začnem odgovarjati med razpravo o načrtovanju zelenih niti. Odgovor zaključim med razpravljanjem o načrtovanju izvornih niti.

Načrtovanje zelenih niti

Vsi operacijski sistemi, na primer starodavni perativni sistem Microsoft Windows 3.1, ne podpirajo niti. Za takšne sisteme lahko Sun Microsystems oblikuje JVM, ki svojo edino nit izvedbe razdeli na več niti. JVM (ne operacijski sistem osnovne platforme) zagotavlja logiko navojev in vsebuje načrtovalnik niti. Niti JVM so zelene niti, ali uporabniške niti.

Načrtovalnik niti JVM razporeja zelene niti v skladu z prednostna naloga—Relativni pomen niti, ki ga izrazite kot celo število iz natančno določenega obsega vrednosti. Načrtovalnik niti JVM običajno izbere nit z najvišjo prioriteto in dovoli, da se nit izvaja, dokler se ne konča ali blokira. Takrat načrtovalnik niti izbere nit naslednje najvišje prioritete. Ta nit (običajno) teče, dokler se ne konča ali blokira. Če se med zagonom niti odblokira nit z višjo prioriteto (morda je potekel čas mirovanja niti z večjo prioriteto), načrtovalnik niti predpostavke, ali prekine nit nižje prioritete in procesorju dodeli odblokirano nit višje prioritete.

Opomba: Nit, ki jo je mogoče zagnati z najvišjo prioriteto, se ne bo vedno zagnal. Tukaj je Specifikacija jezika Java 's prevzemite prednost:

Vsaka nit ima prednostna naloga. Kadar obstaja konkurenca za obdelavo virov, se niti z višjo prioriteto praviloma izvajajo pred nitmi z nižjo prioriteto. Vendar taka prednost ni zagotovilo, da bo nit z najvišjo prioriteto vedno delovala, prednostnih niti pa ni mogoče uporabiti za zanesljivo izvajanje medsebojne izključitve.

Ta sprejem veliko pove o izvajanju zelenih niti JVM. Ti JVM si ne morejo dovoliti, da niti blokirajo, ker bi to povezalo edino nit izvajanja JVM. Torej, kadar mora nit blokirati, na primer, ko ta nit počasi bere podatke, da prispe iz datoteke, lahko JVM ustavi izvajanje niti in z mehanizmom ankete določi, kdaj prispejo podatki. Medtem ko nit ostane zaustavljena, lahko načrtovalnik niti JVM načrtuje zagon niti z nižjo prioriteto. Recimo, da podatki prispejo med izvajanjem niti z nižjo prioriteto. Čeprav bi se nit z višjo prioriteto morala zagnati takoj, ko prispejo podatki, se to zgodi šele, ko JVM naslednjič anketira operacijski sistem in odkrije prihod. Zato se nit z nižjo prioriteto zažene, čeprav bi se nit z višjo prioriteto morala izvajati. Zaradi te situacije morate skrbeti le, če potrebujete Java vedenje v realnem času. Toda Java potem ni operativni sistem v realnem času, zakaj bi torej skrbel?

Če želite razumeti, katera zelena nit, ki jo je mogoče izvajati, postane trenutno zelena nit, upoštevajte naslednje. Recimo, da je vaša aplikacija sestavljena iz treh niti: glavna nit, ki poganja glavni () metoda, nit izračuna in nit, ki bere vnos s tipkovnice. Ko ni tipkovnice, se nit branja blokira. Predpostavimo, da ima bralna nit najvišjo prioriteto, nit izračuna pa najnižjo prioriteto. (Zaradi enostavnosti predpostavimo tudi, da nobena druga notranja nit JVM ni na voljo.) Slika 1 prikazuje izvajanje teh treh niti.

V času T0 se glavna nit začne izvajati. V času T1 glavna nit začne nit izračuna. Ker ima nit za izračun nižjo prednost kot glavna nit, nit za izračun čaka na procesor. V času T2 glavna nit začne bralno nit. Ker ima bralna nit večjo prednost kot glavna nit, glavna nit čaka na procesor, medtem ko se bralna nit izvaja. V času T3 se bralna nit blokira in glavna nit teče. V času T4 se bralna nit odblokira in zažene; glavna nit čaka. Nazadnje, v času T5 se bralni navoj blokira in glavna nit teče. Izmenjava med branjem in glavno nitjo se nadaljuje, dokler se program izvaja. Nit za izračun se nikoli ne zažene, ker ima najnižjo prioriteto in tako strada za pozornost procesorja, situacija znana kot procesorsko stradanje.

Ta scenarij lahko spremenimo tako, da nit izračuna izračuna enako prednost kot glavna nit. Slika 2 prikazuje rezultat, ki se začne s časom T2. (Pred T2 je slika 2 enaka sliki 1.)

V času T2 se bralna nit zažene, medtem ko glavna in računska niti čakajo na procesor. V času T3 se nit branja blokira in nit izračuna izračuna, ker je glavna nit tekla tik pred nitjo branja. V času T4 se bralna nit odblokira in zažene; glavna in računska nit čakajo. V času T5 se bralna nit blokira in glavna nit teče, ker se je izračunska nit izvajala tik pred bralno nitjo. To menjavanje med izvajanjem glavne in računske niti se nadaljuje, dokler se program izvaja in je odvisno od višje prioritetne niti, ki se izvaja in blokira.

Upoštevati moramo še zadnji element pri načrtovanju zelenih niti. Kaj se zgodi, ko nit z nižjo prioriteto ima ključavnico, ki jo zahteva nit z višjo prioriteto? Nit z višjo prioriteto blokira, ker ne more dobiti ključavnice, kar pomeni, da ima nit z višjo prioriteto enako prednost kot nit z nižjo prioriteto. Na primer prednost nit 6 skuša pridobiti ključavnico, ki jo ima nit prioritete 3. Ker mora nit 6 s prednostjo počakati, da lahko pridobi ključavnico, ima nit 6 s prednostjo 3 - pojav, znan kot prednostna inverzija.

Prednostna inverzija lahko močno upočasni izvajanje niti z višjo prioriteto. Denimo, da imate na primer tri niti s prednostnimi nalogami 3, 4 in 9. Prednostna nit 3 se izvaja, druge niti pa so blokirane. Predpostavimo, da prednostna nit 3 zgrabi ključavnico, prednostna nit 4 pa se odblokira. Prednostna nit 4 postane trenutno izvedena nit. Ker nit 9 s prednostjo zahteva zaklepanje, še naprej čaka, da nit 3 s prednostjo sprosti ključavnico. Vendar nit 3 s prednostjo ne more sprostiti ključavnice, dokler nit 4 s prednostjo ne blokira ali zaključi. Rezultat tega je, da prednostna nit 9 zadrži njegovo izvajanje.

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