Programiranje

Zasnova za varnost navojev

Pred šestimi meseci sem začel vrsto člankov o oblikovanju razredov in predmetov. V tem mesecu Tehnike oblikovanja stolpec, nadaljeval bom to serijo, tako da bom preučil načela oblikovanja, ki zadevajo varnost niti. Ta članek vam pove, kaj je varnost niti, zakaj jo potrebujete, kdaj jo potrebujete in kako jo pridobiti.

Kaj je varnost niti?

Varnost niti preprosto pomeni, da polja predmeta ali razreda vedno ohranijo veljavno stanje, kot ga opazijo drugi predmeti in razredi, tudi če jih hkrati uporablja več niti.

Ena prvih smernic, ki sem jih predlagal v tem stolpcu (glejte "Oblikovanje inicializacije objektov"), je, da morate razviti razrede tako, da bodo predmeti ohranili veljavno stanje od začetka njihove življenjske dobe do konca. Če upoštevate ta nasvet in ustvarite predmete, katerih spremenljivke primerka so vse zasebne in katerih metode izvajajo samo ustrezne prehode stanja na teh spremenljivkah primerka, ste v dobrem stanju v enonitnem okolju. Toda morda boste imeli težave, ko se bo pojavilo več niti.

Več niti lahko črka težave za vaš objekt, ker je pogosto, medtem ko je metoda v postopku izvajanja, stanje vašega predmeta začasno neveljavno. Ko samo ena nit prikliče metode predmeta, se bo izvajala samo ena metoda naenkrat in vsaka metoda bo lahko zaključena, preden se prikliče druga metoda. Tako bo v enonitnem okolju vsaka metoda dobila priložnost, da se prepriča, ali se začasno neveljavno stanje spremeni v veljavno, preden se metoda vrne.

Ko uvedete več niti, pa lahko JVM prekine nit, ki izvaja eno metodo, medtem ko so spremenljivke primerka predmeta še vedno v začasno neveljavnem stanju. JVM bi nato lahko dal različni niti priložnost za izvedbo, ta nit pa bi lahko klicala metodo na istem objektu. Vso vaše trdo delo, da spremenljivke primerka naredite zasebne in vaše metode izvajajo samo veljavne transformacije stanja, ne bo zadoščalo, da bi ta druga nit preprečila, da bi objekt opazoval v neveljavnem stanju.

Takšen predmet ne bi bil varen za nit, ker bi se lahko v večnitnem okolju objekt poškodoval ali opazil, da ima neveljavno stanje. Nitko varen objekt je tisti, ki vedno ohranja veljavno stanje, kot so opazili drugi razredi in predmeti, tudi v večnitnem okolju.

Zakaj skrbeti za varnost niti?

Obstajata dva velika razloga, da morate pri načrtovanju razredov in predmetov v Javi razmišljati o varnosti niti:

  1. Podpora za več niti je vgrajena v jezik Java in API

  2. Vse niti znotraj navideznega stroja Java (JVM) imajo isti kup in območje metode

Ker je večnitnost vgrajena v Javo, je možno, da lahko kateri koli razred, ki ga načrtujete, hkrati uporablja več niti. Ni vam treba (in ne bi smeli) zagotoviti, da bi bil vsak razred, ki ga načrtujete, varen za nit, saj varnost niti ni na voljo brezplačno. Ampak morali bi vsaj pomisli o varnosti niti vsakič, ko oblikujete razred Java. V nadaljevanju tega članka boste našli razpravo o stroških varnosti niti in smernice o tem, kdaj razrede narediti varne za nit.

Glede na arhitekturo JVM se morate s spremenljivkami primerka in razreda ukvarjati le, če vas skrbi varnost niti. Ker imajo vse niti isti kup in je na tem mestu shranjena vsa spremenljivka primerka, lahko več niti istočasno poskuša uporabiti spremenljivke primerka istega predmeta. Ker imajo vse niti isto področje metode, območje metode pa je tam, kjer so shranjene vse spremenljivke razreda, lahko več niti istočasno poskuša uporabiti iste spremenljivke razreda. Ko se odločite, da boste razred naredili varnim za nit, je vaš cilj zagotoviti celovitost - v večnitnem okolju - spremenljivk primerka in razreda, razglašenih v tem razredu.

Ni vam treba skrbeti za večnitni dostop do lokalnih spremenljivk, parametrov metode in vrnjenih vrednosti, ker se te spremenljivke nahajajo v kupu Java. V JVM je vsaki niti dodeljen lasten sklad Java. Nobena nit ne more videti ali uporabiti nobene lokalne spremenljivke, vrnjene vrednosti ali parametrov, ki pripadajo drugi niti.

Glede na strukturo JVM so lokalne spremenljivke, parametri metode in vrnjene vrednosti same po sebi "varne za nit". Toda spremenljivke primerka in spremenljivke razreda bodo varne pred nitmi le, če boste razred pravilno oblikovali.

RGBColor # 1: Pripravljen za eno nit

Kot primer razreda, ki je ne varno pred nitmi, upoštevajte RGBColor razred, prikazan spodaj. Primerki tega razreda predstavljajo barvo, shranjeno v treh spremenljivkah zasebnega primerka: r, g, in b. Glede na spodaj prikazan razred, an RGBColor objekt bi začel svoje življenje v veljavnem stanju in bi doživljal le prehode v veljavnem stanju, od začetka svojega življenja do konca - vendar le v enonitnem okolju.

// V datotečnih nitih / ex1 / RGBColor.java // Primerki tega razreda NISO varni za nit. javni razred RGBColor {private int r; zasebni int g; zasebni int b; javni RGBColor (int r, int g, int b) {checkRGBVals (r, g, b); to.r = r; to.g = g; to.b = b; } public void setColor (int r, int g, int b) {checkRGBVals (r, g, b); to.r = r; to.g = g; to.b = b; } / ** * vrne barvo v polju treh intov: R, G in B * / public int [] getColor () {int [] retVal = new int [3]; retVal [0] = r; retVal [1] = g; retVal [2] = b; vrnitev retVal; } javna void invert () {r = 255 - r; g = 255 - g; b = 255 - b; } zasebna statična void checkRGBVals (int r, int g, int b) {if (r 255 || g 255 || b <0 || b> 255) {throw new IllegalArgumentException (); }}} 

Ker tri spremenljivke, ints r, g, in bso zasebni, edini način, kako lahko drugi razredi in predmeti dostopajo do vrednosti teh spremenljivk ali vplivajo nanje, je RGBColorkonstruktor in metode. Zasnova konstruktorja in metode zagotavljajo, da:

  1. RGBColorkonstruktor vedno da spremenljivkam ustrezne začetne vrednosti

  2. Metode setColor () in invert () bo za te spremenljivke vedno izvedel veljavne transformacije stanja

  3. Metoda getColor () bo vedno vrnil veljaven pogled teh spremenljivk

Upoštevajte, da če se slabi podatki posredujejo konstruktorju ali setColor () , bodo nenadoma dopolnili z InvalidArgumentException. The checkRGBVals () metoda, ki vrže to izjemo, dejansko določa, kaj pomeni za RGBColor veljaven objekt: vrednosti vseh treh spremenljivk, r, g, in b, mora biti med 0 in 255 vključno z. Poleg tega mora biti barva, ki jo predstavljajo te spremenljivke, najnovejša barva, ki je bila posredovana konstruktorju ali setColor () ali izdelano s pomočjo invert () metoda.

Če v enonitnem okolju prikličete setColor () in prehod v modri barvi RGBColor predmet bo modre barve, ko setColor () vrne. Če nato prikličete getColor () na istem predmetu boste dobili modro. V družbi z eno nitjo primeri tega RGBColor razreda dobro obnašajo.

Metanje sočasnega ključa v dela

Na žalost je ta vesela slika lepo vzgojene RGBColor Predmet lahko postane strašen, ko v sliko vstopijo druge niti. V okolju z več nitmi so primeri RGBColor zgoraj definirani razred so dovzetni za dve vrsti slabega vedenja: pisanje / pisanje konfliktov in branje / pisanje konfliktov.

Pisanje / pisanje konfliktov

Predstavljajte si, da imate dve niti, eno nit z imenom "rdeča" in drugo z imenom "modra". Obe niti poskušata nastaviti barvo iste RGBColor objekt: Rdeča nit poskuša barvo nastaviti na rdečo; modra nit poskuša barvo nastaviti na modro.

Obe niti poskušata hkrati zapisovati v spremenljivke primerka istega predmeta. Če načrtovalnik niti preplete te dve niti na pravi način, bosta niti nehote posegali med seboj, kar bo povzročilo konflikt pisanja / pisanja. Med tem bosta niti pokvarili stanje predmeta.

The Nesinhronizirano RGBColor aplet

Naslednji programček z imenom Nesinhronizirana RGBColor, prikazuje eno zaporedje dogodkov, ki bi lahko povzročili poškodbe RGBColor predmet. Rdeča nit nedolžno poskuša nastaviti barvo na rdečo, modra nit pa nedolžno poskuša nastaviti barvo na modro. Na koncu je RGBColor Predmet ne predstavlja niti rdeče niti modre, temveč vznemirjajočo barvo, magenta.

Iz nekega razloga vam brskalnik ne dovoli, da na ta način vidite kul Java programček.

Stopati skozi zaporedje dogodkov, ki vodijo do poškodovanega RGBColor pritisnite gumb Step koraka. Pritisnite Back za varnostno kopiranje koraka in Reset za varnostno kopiranje na začetek. Ko nadaljujete, bo vrstica besedila na dnu programčka razložila, kaj se dogaja v vsakem koraku.

Za tiste, ki ne morete zagnati programčka, je tukaj tabela, ki prikazuje zaporedje dogodkov, ki jih prikazuje programček:

NavojIzjavargbBarva
nobenegapredmet predstavlja zeleno02550 
modramodra nit prikliče setColor (0, 0, 255)02550 
modracheckRGBVals (0, 0, 255);02550 
modrato.r = 0;02550 
modrato.g = 0;02550 
modramodra dobi prednost000 
rdečardeča nit prikliče setColor (255, 0, 0)000 
rdečacheckRGBVals (255, 0, 0);000 
rdečato.r = 255;000 
rdečato.g = 0;25500 
rdečato.b = 0;25500 
rdečardeča nit se vrne25500 
modrakasneje se modra nit nadaljuje25500 
modrato.b = 25525500 
modravrne se modra nit2550255 
nobenegapredmet predstavlja magenta2550255 

Kot lahko vidite iz tega programčka in tabele, RGBColor je poškodovan, ker načrtovalnik niti prekine modro nit, medtem ko je objekt še vedno v začasno neveljavnem stanju. Ko pride rdeča nit in predmet pobarva v rdečo barvo, modra nit le delno dokonča barvanje predmeta v modro. Ko se modra nit vrne, da konča opravilo, nehote poškoduje predmet.

Brisanje / pisanje konfliktov

Druga vrsta neprimernega vedenja, ki je lahko razvidno iz primerov tega v večnitnem okolju RGBColor razred je branje / pisanje konfliktov. Ta vrsta navzkrižja nastane, ko je stanje predmeta prebrano in uporabljeno v začasno neveljavnem stanju zaradi nedokončanega dela druge niti.

Na primer, upoštevajte, da med izvajanjem modre niti setColor () Z zgornjo metodo se objekt na enem mestu znajde v začasno neveljavnem stanju črne barve. Tu je črna začasno neveljavno stanje, ker:

  1. Začasno je: sčasoma namerava modra nit barvo nastaviti na modro.

  2. Neveljavno: Nihče ni prosil za črno RGBColor predmet. Modra nit naj bi zeleni predmet spremenila v modro.

Če je modra nit trenutno prevzeta, objekt predstavlja črno z nitjo, ki prikliče getColor () na istem predmetu bi ta druga nit opazovala RGBColor vrednost predmeta je črna.

Tu je tabela, ki prikazuje zaporedje dogodkov, ki bi lahko privedli do prav takšnega konflikta med branjem in pisanjem:

NavojIzjavargbBarva
nobenegapredmet predstavlja zeleno02550 
modramodra nit prikliče setColor (0, 0, 255)02550 
modracheckRGBVals (0, 0, 255);02550 
modrato.r = 0;02550 
modrato.g = 0;02550 
modramodra dobi prednost000 
rdečardeča nit prikliče getColor ()000 
rdečaint [] retVal = nov int [3];000 
rdečaretVal [0] = 0;000 
rdečaretVal [1] = 0;000 
rdečaretVal [2] = 0;000 
rdečavrnitev retVal;000 
rdečardeča nit vrne črno000 
modrakasneje se modra nit nadaljuje000 
modrato.b = 255000 
modravrne se modra nit00255 
nobenegaPredmet predstavlja modro00255 

Kot lahko vidite iz te tabele, se težava začne, ko se modra nit prekine, ko je le delno končal barvanje predmeta v modro. Na tej točki je objekt v začasno neveljavnem stanju črne barve, kar je točno tisto, kar rdeča nit vidi, ko se pokliče getColor () na predmetu.

Trije načini, kako narediti predmet varnim za nit

V bistvu obstajajo trije pristopi za izdelavo predmeta, kot je RGBThread varno na nit:

  1. Sinhronizirajte kritične odseke
  2. Naj bo nespremenljiva
  3. Uporabite ovoj, ki je varen z nitmi

Pristop 1: Sinhronizacija kritičnih odsekov

Najbolj neposreden način popravljanja neposlušnega vedenja, ki ga kažejo predmeti, kot so RGBColor kadar je postavljen v kontekst z več nitmi, je sinhronizacija kritičnih odsekov predmeta. Predmet kritični odseki so tiste metode ali bloki kode znotraj metod, ki jih mora hkrati izvajati samo ena nit. Povedano drugače, kritični odsek je metoda ali blok kode, ki mora biti izveden atomsko kot ena, nedeljiva operacija. Z uporabo Jave sinhronizirano s ključno besedo lahko zagotovite, da bo samo ena nit hkrati izvajala kritične odseke predmeta.

Če želite uporabiti ta pristop k temu, da naredite svoj objekt varnim za nit, morate slediti dvema korakoma: vsa ustrezna polja morate narediti zasebna ter prepoznati in sinhronizirati vse kritične odseke.

1. korak: Polja naredite zasebna

Sinhronizacija pomeni, da bo lahko samo ena nit naenkrat izvedla bit kode (kritični odsek). Torej, čeprav je polja želite koordinirati dostop do več niti, Javin mehanizem za to dejansko koordinira dostop do Koda. To pomeni, da boste le, če boste podatke naredili zasebne, lahko nadzorovali dostop do teh podatkov z nadzorom dostopa do kode, ki manipulira s podatki.

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