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:
Podpora za več niti je vgrajena v jezik Java in API
- 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, int
s r
, g
, in b
so zasebni, edini način, kako lahko drugi razredi in predmeti dostopajo do vrednosti teh spremenljivk ali vplivajo nanje, je RGBColor
konstruktor in metode. Zasnova konstruktorja in metode zagotavljajo, da:
RGBColor
konstruktor vedno da spremenljivkam ustrezne začetne vrednostiMetode
setColor ()
ininvert ()
bo za te spremenljivke vedno izvedel veljavne transformacije stanja- 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.
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:
Navoj | Izjava | r | g | b | Barva |
nobenega | predmet predstavlja zeleno | 0 | 255 | 0 | |
modra | modra nit prikliče setColor (0, 0, 255) | 0 | 255 | 0 | |
modra | checkRGBVals (0, 0, 255); | 0 | 255 | 0 | |
modra | to.r = 0; | 0 | 255 | 0 | |
modra | to.g = 0; | 0 | 255 | 0 | |
modra | modra dobi prednost | 0 | 0 | 0 | |
rdeča | rdeča nit prikliče setColor (255, 0, 0) | 0 | 0 | 0 | |
rdeča | checkRGBVals (255, 0, 0); | 0 | 0 | 0 | |
rdeča | to.r = 255; | 0 | 0 | 0 | |
rdeča | to.g = 0; | 255 | 0 | 0 | |
rdeča | to.b = 0; | 255 | 0 | 0 | |
rdeča | rdeča nit se vrne | 255 | 0 | 0 | |
modra | kasneje se modra nit nadaljuje | 255 | 0 | 0 | |
modra | to.b = 255 | 255 | 0 | 0 | |
modra | vrne se modra nit | 255 | 0 | 255 | |
nobenega | predmet predstavlja magenta | 255 | 0 | 255 |
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:
Začasno je: sčasoma namerava modra nit barvo nastaviti na modro.
- 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:
Navoj | Izjava | r | g | b | Barva |
nobenega | predmet predstavlja zeleno | 0 | 255 | 0 | |
modra | modra nit prikliče setColor (0, 0, 255) | 0 | 255 | 0 | |
modra | checkRGBVals (0, 0, 255); | 0 | 255 | 0 | |
modra | to.r = 0; | 0 | 255 | 0 | |
modra | to.g = 0; | 0 | 255 | 0 | |
modra | modra dobi prednost | 0 | 0 | 0 | |
rdeča | rdeča nit prikliče getColor () | 0 | 0 | 0 | |
rdeča | int [] retVal = nov int [3]; | 0 | 0 | 0 | |
rdeča | retVal [0] = 0; | 0 | 0 | 0 | |
rdeča | retVal [1] = 0; | 0 | 0 | 0 | |
rdeča | retVal [2] = 0; | 0 | 0 | 0 | |
rdeča | vrnitev retVal; | 0 | 0 | 0 | |
rdeča | rdeča nit vrne črno | 0 | 0 | 0 | |
modra | kasneje se modra nit nadaljuje | 0 | 0 | 0 | |
modra | to.b = 255 | 0 | 0 | 0 | |
modra | vrne se modra nit | 0 | 0 | 255 | |
nobenega | Predmet predstavlja modro | 0 | 0 | 255 |
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:
- Sinhronizirajte kritične odseke
- Naj bo nespremenljiva
- 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.