Programiranje

JavaBeans: lastnosti, dogodki in varnost niti

Java je dinamičen jezik, ki vključuje enostavne večnitne jezikovne konstrukcije in razrede podpore. Številni programi Java se zanašajo na večnitnost, da bi izkoristili vzporednost notranje aplikacije, izboljšali zmogljivost omrežja ali pospešili odziv uporabniških povratnih informacij. Večina časa izvajanja Java uporablja večnitnost za izvajanje funkcije Jave za zbiranje smeti. Končno se AWT za svoje delovanje opira tudi na ločene niti. Skratka, tudi najpreprostejši programi Java se rodijo v okolju z več nitmi.

Zato so tudi fižol Java v takšnem dinamičnem okolju z več nitmi in v tem je klasična nevarnost, da naletimo na dirkalni pogoji. Dirkalni pogoji so od časa odvisni scenariji pretoka programov, ki lahko vodijo do korupcije stanja (podatkov o programu). V naslednjem poglavju bom podrobno opisal dva takšna scenarija. Vsak grah Java mora biti zasnovan z upoštevanjem dirkalnih pogojev, tako da lahko grah prenese hkratno uporabo več odjemalskih niti.

Težave z več nitmi s preprostimi lastnostmi

Izvedbe graha morajo predvidevati, da več niti hkrati dostopa do in / ali spreminja en primerek graha. Kot primer nepravilno izvedenega fižola (ker se nanaša na ozaveščenost o večnitnostih) razmislite o naslednjem fižolu BrokenProperties in z njim povezanem testnem programu MTProperties:

BrokenProperties.java

uvoz java.awt.Point;

// Demo Bean, ki ne varuje pred uporabo več niti.

javni razred BrokenProperties podaljša Point {

// ------------------------------------------------ ------------------- // set () / get () za lastnost 'Spot' // --------------- -------------------------------------------------- -

public void setSpot (Point point) {// 'spot' setter this.x = point.x; this.y = point.y;

} public Point getSpot () {// 'spot' getter vrne to; }} // Konec fižola / razreda BrokenProperties

MTProperties.java

uvoz java.awt.Point; uvozne pripomočke. *; uvoz utilities.beans. *;

javni razred MTProperties razširja Thread {

zaščitena BrokenProperties myBean; // ciljni fižol za bash ..

zaščiten int myID; // vsaka nit vsebuje malo ID-ja

// ------------------------------------------------ ------------------- // glavna () vstopna točka // ---------------------- --------------------------------------------- javna statična void glavna ( String [] args) {

Fižol BrokenProperties; Nit niti;

bean = (BrokenProperties) BeansKit.newBean ("BrokenProperties");

for (int i = 0; i <20; i ++) {// zaženi 20 niti za bash nit fižola = nove MTProperties (bean, i); // niti pridobijo dostop do bean-a thread.start (); }} // ---------------------------------------------- --------------------- // Konstruktor MTProperties // ----------------------- --------------------------------------------

javne MTProperties (BrokenProperties bean, int id) {this.myBean = bean; // opomnimo fižol, da naslovi to.myID = id; // opazimo, kdo smo} // ----------------------------------------- -------------------------- // glavna zanka niti: // do forever // ustvari novo naključno točko z x == y // povej beanu, naj sprejme Point kot svojo novo lastnost 'spot' // vprašaj bean, na kaj je zdaj nastavljena njegova lastnost 'spot' // vrže nihanje, če spot x ni enak mestu y // --------- -------------------------------------------------- -------- public void run () {int someInt; Točka točka = nova točka ();

while (true) {someInt = (int) (Math.random () * 100); point.x = someInt; point.y = someInt; myBean.setSpot (točka);

point = myBean.getSpot (); if (point.x! = point.y) {System.out.println ("Bean poškodovan! x =" + point.x + ", y =" + point.y); System.exit (10); } System.out.print ((char) ('A' + myID)); System.out.flush (); }}} // Konec razreda MTProperties

Opomba: Paket pripomočkov, ki ga je uvozil Lastnosti MTP vsebuje razrede za večkratno uporabo in statične metode, ki jih je za knjigo razvil avtor.

Zgornja dva seznama izvornih kod definirata grah z imenom BrokenProperties in razred Lastnosti MTP, ki se uporablja za vadbo fižola znotraj 20 tekočih niti. Sledimo Lastnosti MTP' glavni () vstopna točka: Najprej ustvari zrn BrokenProperties, čemur sledi ustvarjanje in zagon 20 niti. Razred Lastnosti MTP podaljša java.lang.Nit, torej vse, kar moramo storiti, da spremenimo razred Lastnosti MTP v nit je preglasiti razred Navojje teči () metoda. Konstruktor za naše niti ima dva argumenta: bean objekt, s katerim bo nit komunicirala, in edinstveno identifikacijo, ki omogoča enostavno razlikovanje 20 niti med izvajanjem.

Poslovni konec te predstavitve je naš teči () metoda pri pouku Lastnosti MTP. Tu se za vedno zankamo in ustvarjamo naključne nove (x, y) točke, vendar z naslednjo značilnostjo: njihova koordinata x je vedno enaka njihovi koordinati y. Te naključne točke se posredujejo fižolu setSpot () in nato takoj preberite znova z uporabo getSpot () getter metoda. Pričakovali bi branje spot lastnost, da je enaka naključni točki, ustvarjeni pred nekaj milisekundami. Tu je vzorec izhoda programa, ki ga prikličete v ukazni vrstici:

ABBBBBBBBBBBBBBBBBBDJJJJJJJJJJJJJJJJJJJJEGHHHHHHHHHHHHHHHHHHSSSSSSSSSSSSSS IICCBBBBBBBBBBBBBBBBBKBDLJMBOPLQNRTPHHHHHHHHFFFFFFFFFFFFSSSSSSSSSSSSSSSSSS FFFFFFFFFFFFFFFAAAAAACCCCCCCKKKKKKKKKKKKKKKKKMMMMMMMMMMMMMMMMMMMMMMMMMMMDD JEOQQQQQQQQQQQQQQQRRRRRRRRRRRRRRRRRBBBBBBBBBBBBBBBTTTTTTTTTTTTTTTTLPPPPPPP PPPPGGHHHFFFFFFFFIIIIIIIIIIIIIISSSSSSSSSSSSSSSSSSSSACCCCCCCCCCCCCCCCCCCKMD QQQQQQNNNNNNNNNNNNNNNNRRRRRTRRHHHHHHHHHFFFFFFFFFFFFFFFFFFIIIIIIIIIIIIIIIII MMMJEEEEEEEEEEDDDDEEEEEEEEEOOOOOOOOOOOOOOOOOOOOOOOOOOOQNNNNNNNNBTLPLRGFFFF FFFFFFFFFIIAAAAAAAAAAAAAAAAASSSSSSSSSSSSSSSSSSKKKKKKKKKKKKKKKKCCCCCCCMMJAA AACBean poškodovan! x = 67, y = 13 OOOOOOOOOOOOOOOOOOO 

Rezultat prikazuje vzporednih 20 niti (kolikor gre za človeškega opazovalca); vsaka nit uporabi ID, ki ga je prejela v času izdelave, za tiskanje ene od črk A do T, prvih 20 črk abecede. Takoj, ko katera nit odkrije, da je branje nazaj spot lastnost ne ustreza programirani značilnosti x = y, nit natisne sporočilo "Bean corrupted" in zaustavi poskus.

Kar opazite, je stranski učinek dirkalnega stanja znotraj fižola, ki kvari državo setSpot () Koda. Tu je spet ta metoda:

public void setSpot (Point point) {// 'spot' setter this.x = point.x; this.y = point.y; } 

Kaj bi lahko kdaj šlo narobe v tako preprosti bitni kodi? Predstavljajte si nit A klicanje setSpot () s točkovnim argumentom, enakim (67,67). Če zdaj upočasnimo uro vesolja, tako da lahko vidimo, kako navidezni stroj Java (JVM) izvaja vsak stavek Java, enega za drugim, si lahko predstavljamo nit A izvajanje izjave o kopiji koordinat x (to.x = točka.x;) in nato nenadoma nit A - zamrzne operacijski sistem in nit C je predviden za nekaj časa. V prejšnjem delujočem stanju nit C je pravkar ustvaril svojo novo naključno točko (13,13), imenovano setSpot () in se nato zamrznil, da bi naredil prostor nit M, takoj po tem, ko je postavil koordinato x na 13. Torej, nadaljevanje nit C zdaj nadaljuje s svojo programirano logiko: nastavi y na 13 in preveri, ali je spot lastnost enaka (13, 13), vendar ugotovi, da se je skrivnostno spremenila v nedovoljeno stanje (67, 13); koordinata x je polovica stanja česa nit A je bila nastavitev spot do, koordinata y pa je polovica stanja česa nit Cje nastavilspot do. Končni rezultat je, da ima zrn BrokenProperties notranje nedosledno stanje: pokvarjeno lastnost.

Kadar koli neatomska podatkovno strukturo (torej strukturo, sestavljeno iz več kot enega dela) lahko hkrati spreminja več niti, strukturo morate zaščititi s ključavnico. V Javi se to naredi z uporabo sinhronizirano ključna beseda.

Opozorilo: Za razliko od vseh drugih vrst Java upoštevajte, da Java tega ne zagotavlja dolga in dvojno se obravnavajo atomsko! To je zato, ker dolga in dvojno zahtevajo 64 bitov, kar je dvakrat več kot dolžina besede sodobne arhitekture CPU (32 bitov). Nalaganje in shranjevanje posameznih strojnih besed sta po naravi atomska operacija, vendar je za premikanje 64-bitnih entitet potrebna dva taka premika, ki ju Java ne zaščiti iz običajnega razloga: zmogljivosti. (Nekateri procesorji omogočajo zaklepanje sistemskega vodila za izvajanje večbesednega prenosa atomsko, vendar ta možnost ni na voljo v vseh CPU-jih in bi bila v vsakem primeru izjemno draga za vse dolga ali dvojno manipulacije!) Torej, tudi če lastnost sestavlja samo ena sama dolga ali samski dvojno, uporabite polne varnostne ukrepe za zaklepanje, da zaščitite svoje dolžine ali dvojnike pred nenadnimi popolnimi poškodbami.

The sinhronizirano Ključna beseda označuje blok kode kot atomski korak. Kode ni mogoče "razdeliti", kot kadar druga nit prekine kodo, da bi jo lahko ponovno vstavil (torej izraz povratna koda; vsa koda Java mora biti ponovno vnesena). Rešitev za naš fižol BrokenProperties je trivialna: samo zamenjajte ga setSpot () metoda s tem:

public void setSpot (Point point) {// sinhronizirano (to) setter 'spot' {this.x = point.x; this.y = point.y; }} 

Ali drugače s tem:

javna sinhronizirana praznina setSpot (Point point) {// 'spot' setter this.x = point.x; this.y = point.y; } 

Obe zamenjavi sta popolnoma enakovredni, čeprav imam raje prvi slog, ker jasneje prikazuje, kakšna je natančna funkcija sinhronizirano ključna beseda je: sinhronizirani blok je nenehno povezan s predmetom, ki se zaklene. Avtor zaklenjeno Mislim, da JVM najprej poskuša pridobiti ključavnico (torej izključni dostop) do predmeta (torej pridobiti ekskluziven dostop do njega) ali počaka, da se objekt odklene, če ga je zaklenila druga nit. Postopek zaklepanja zagotavlja, da lahko kateri koli predmet hkrati zaklene (ali je v lasti) samo ena nit.

Torej sinhronizirano (to) sintaksa jasno odraža notranji mehanizem: Argument v oklepaju je predmet, ki ga je treba zakleniti (trenutni objekt), preden vnesete blok kode. Alternativna sintaksa, kjer je sinhronizirano Ključna beseda se uporablja kot modifikator v podpisu metode in je preprosto okrajšana različica prve.

Opozorilo: Ko so označene statične metode sinhronizirano, ni to predmet za zaklepanje; samo trenutne metode so povezane s trenutnim objektom. Torej, ko so metode razredov sinhronizirane, java.lang.Class objekt, ki ustreza razredu metode, se namesto tega uporablja za zaklepanje. Ta pristop ima resne posledice za uspešnost, ker zbirka primerkov razreda deli en sam povezan Razred predmet; kadarkoli to Razred objekt se zaklene, vsem objektom tega razreda (bodisi 3, 50 ali 1000!) je prepovedano priklicati isto statično metodo. S tem v mislih morate dvakrat premisliti, preden uporabite sinhronizacijo s statičnimi metodami.

V praksi se vedno spomnite izrecne sinhronizirane oblike, ker vam omogoča, da "atomizirate" najmanjši možni blok kode znotraj metode. Stenografska oblika "atomizira" celotno metodo, kar je zaradi učinkovitosti pogosto ne Kaj želiš. Ko je nit vstopila v atomski blok kode, nobena druga nit ni potrebna kaj sinhronizirana koda na istem objektu.

Namig: Ko na predmetu dobimo ključavnico, potem vse sinhronizirana koda za razred tega predmeta bo postala atomska. Če torej vaš razred vsebuje več kot eno podatkovno strukturo, ki jo je treba obdelati atomsko, vendar so te podatkovne strukture drugačne neodvisen drug drugega, potem lahko nastane še eno ozko grlo pri izvedbi. Odjemalci, ki pokličejo sinhronizirane metode, ki manipulirajo z eno notranjo podatkovno strukturo, bodo blokirali vse druge odjemalce, ki pokličejo druge metode, ki obravnavajo katere koli druge atomske podatkovne strukture vašega razreda. Jasno je, da se takim situacijam izogibajte tako, da razred razdelite na manjše razrede, ki obdelujejo samo eno podatkovno strukturo, ki jo je treba hkrati obdelati atomsko.

JVM izvaja svojo sinhronizacijsko funkcijo z ustvarjanjem čakalnih vrst niti, ki čakajo, da se objekt odklene. Čeprav je ta strategija odlična, ko gre za zaščito doslednosti sestavljenih podatkovnih struktur, lahko povzroči večnitne prometne zastoje, ko je manj učinkovit odsek kode označen kot sinhronizirano.

Zato bodite vedno pozorni na to, koliko kode sinhronizirate: to mora biti nujni minimum. Na primer, predstavljajte si našo setSpot () prvotno sestavljena iz:

javna praznina setSpot (Point point) {// 'spot' setter log.println ("setSpot () pozvan" + this.toString ()); to.x = točka.x; this.y = point.y; } 

Čeprav je println izjava lahko logično spadajo v setSpot () metoda ni del zaporedja stavkov, ki ga je treba združiti v atomsko celoto. Zato je v tem primeru pravi način za uporabo sinhronizirano ključna beseda bi bila naslednja:

javna praznina setSpot (Point point) {// 'spot' setter log.println ("setSpot () pozvan" + this.toString ()); sinhronizirano (to) {this.x = point.x; this.y = point.y; }} 

"Leni" način in pristop, ki se mu morate izogibati, je videti takole:

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