Prieš šešis mėnesius pradėjau straipsnių ciklą apie klasių ir objektų projektavimą. Šio mėnesio Dizaino technika stulpelį, aš tęsiu tą seriją, atsižvelgdamas į dizaino principus, susijusius su siūlų sauga. Šiame straipsnyje pasakojama, kas yra siūlų sauga, kodėl jums to reikia, kada reikia ir kaip elgtis.
Kas yra siūlų sauga?
Siūlų sauga paprasčiausiai reiškia, kad objekto ar klasės laukai visada išlaiko galiojančią būseną, kaip pastebi kiti objektai ir klasės, net jei tuo pačiu metu naudojami keli siūlai.
Viena iš pirmųjų gairių, kurią pasiūliau šiame stulpelyje (žr. „Objektų inicializavimo projektavimas“), yra tai, kad turėtumėte suprojektuoti klases taip, kad objektai išlaikytų galiojančią būseną nuo jų gyvenimo pradžios iki pabaigos. Jei laikysitės šio patarimo ir sukursite objektus, kurių egzempliorių kintamieji yra privatūs ir kurių metodai tik tinkamai pakeičia būsenos kintamuosius, esate geros formos vienos gijos aplinkoje. Bet gali kilti nemalonumų, kai atsiras daugiau gijų.
Kelios gijos gali sukelti jūsų objekto problemų, nes dažnai, kol metodas vykdomas, objekto būsena gali būti laikinai negaliojanti. Kai tik viena gija naudoja objekto metodus, vienu metu bus vykdomas tik vienas metodas ir kiekvienam metodui bus leidžiama baigti prieš pradedant naudoti kitą metodą. Taigi vienos gijos aplinkoje kiekvienam metodui bus suteikta galimybė įsitikinti, kad bet kuri laikinai negaliojanti būsena bus pakeista į galiojančią būseną, kol metodas grįš.
Įvedus kelias gijas, JVM gali nutraukti gijų vykdymą vienu metodu, o objekto egzemplioriaus kintamieji vis dar laikinai negaliojančioje būsenoje. Tada JVM galėtų suteikti galimybę kitai gijai įvykdyti, ir ta gija gali iškviesti metodą tam pačiam objektui. Viso jūsų sunkaus darbo, kad jūsų egzempliorių kintamieji būtų privatūs, o jūsų metodai atliktų tik galiojančias būsenos transformacijas, nepakaks, kad ši antroji gija negalėtų stebėti objekto negaliojančioje būsenoje.
Toks objektas nebūtų saugus siūlams, nes daugialypėje aplinkoje objektas gali būti sugadintas arba pastebėtas negaliojantis. Saugus siūlus objektas yra tas, kuris visada palaiko galiojančią būseną, kaip pastebi kitos klasės ir objektai, net ir daugialypėje aplinkoje.
Kodėl verta nerimauti dėl siūlų saugumo?
Kuriant „Java“ klases ir objektus, reikia galvoti apie siūlų saugumą dėl dviejų pagrindinių priežasčių:
Kelių gijų palaikymas yra integruotas į „Java“ kalbą ir API
- Visos „Java“ virtualiosios mašinos (JVM) gijos turi tą patį kaupo ir metodo sritį
Kadangi „Java“ yra integruotas daugialypis gijimas, gali būti, kad bet kurią jūsų suprojektuotą klasę ilgainiui gali naudoti kelios gijos. Nereikia (ir neturėtumėte) padaryti visų jūsų kuriamų klasių siūlų saugiomis, nes siūlų sauga nėra nemokama. Bet jūs bent jau turėtumėte pagalvok apie siūlų saugumą kiekvieną kartą kuriant „Java“ klasę. Vėliau šiame straipsnyje rasite diskusiją apie siūlų saugumo sąnaudas ir gaires, kada klases paversti saugiomis siūlais.
Atsižvelgiant į JVM architektūrą, jums rūpi tik egzempliorių ir klasės kintamieji, kai nerimaujate dėl siūlų saugumo. Kadangi visos gijos turi tą patį krūvą, o krūvoje yra visi egzempliorių kintamieji, kelios gijos gali bandyti vienu metu naudoti to paties objekto egzempliorių kintamuosius. Panašiai, kadangi visos gijos turi tą patį metodo plotą, o metodo srityje yra visi klasės kintamieji, kelios gijos gali bandyti vienu metu naudoti tuos pačius klasės kintamuosius. Kai pasirenkate, kad klasė būtų saugi, jūsų tikslas yra garantuoti toje klasėje deklaruotų pavyzdžių ir klasės kintamųjų vientisumą daugialypėje aplinkoje.
Jums nereikia jaudintis dėl kelių gijų prieigos prie vietinių kintamųjų, metodo parametrų ir grąžinimo reikšmių, nes šie kintamieji yra „Java“ šūsnyje. JVM kiekviena gija yra apdovanota savo „Java“ kaminu. Nė viena gija negali matyti ar naudoti jokių vietinių kintamųjų, grąžinimo verčių ar parametrų, priklausančių kitai gijai.
Atsižvelgiant į JVM struktūrą, vietiniai kintamieji, metodo parametrai ir grąžinimo vertės yra savaime „saugios gijoms“. Tačiau egzempliorių ir klasių kintamieji bus saugūs tik tada, jei tinkamai sukursite savo klasę.
RGBColor # 1: Parengta vienai gijai
Kaip klasės, kuri yra ne siūlai saugūs, apsvarstykite RGBSpalva
klasė, parodyta žemiau. Šios klasės egzemplioriai atspindi spalvą, saugomą trijuose privačių egzempliorių kintamuosiuose: r
, g
ir b
. Atsižvelgiant į žemiau pateiktą klasę, an RGBSpalva
objektas pradėtų savo gyvenimą tinkamoje būsenoje ir patirtų tik galiojančios būsenos perėjimus nuo savo gyvenimo pradžios iki pabaigos, bet tik vienos srities aplinkoje.
// Failų gijose / ex1 / RGBColor.java // Šios klasės egzemplioriai nėra saugūs gijoms. public class RGBColor {private int r; privatus int g; privatus int b; public RGBColor (int r, int g, int b) {checkRGBVals (r, g, b); tai.r = r; tai.g = g; tai.b = b; } public void setColor (int r, int g, int b) {checkRGBVals (r, g, b); tai.r = r; tai.g = g; tai.b = b; } / ** * grąžina spalvą trijų intų masyve: R, G ir B * / public int [] getColor () {int [] retVal = new int [3]; retVal [0] = r; retVal [1] = g; retVal [2] = b; grįžti retVal; } public void invert () {r = 255 - r; g = 255 - g; b = 255 - b; } privatus statinis tuštumos tikrinimasRGBVals (int r, int g, int b) {if (r 255 || g 255 || b <0 || b> 255) {mesti naują NelegaliosArgumentException (); }}}
Kadangi trys egzempliorių kintamieji, tarpt
s r
, g
ir b
, yra privatūs, vienintelis būdas, kuriuo kitos klasės ir objektai gali pasiekti ar paveikti šių kintamųjų reikšmes, yra RGBSpalva
konstruktorius ir metodai. Konstruktoriaus dizainas ir metodai garantuoja, kad:
RGBSpalva
konstruktorius visada duos kintamiesiems tinkamas pradines reikšmesMetodai
setColor ()
irapversti ()
visada atliks galiojančias šių kintamųjų būsenos transformacijas- Metodas
„getColor“ ()
visada pateiks galiojantį šių kintamųjų rodinį
Atkreipkite dėmesį, kad jei netinkami duomenys perduodami konstruktoriui arba setColor ()
metodas, jie bus staiga užbaigti naudojant InvalidArgumentException
. checkRGBVals ()
metodas, kuris išmeta šią išimtį, iš tikrųjų apibrėžia, ką jis reiškia RGBSpalva
objektas galioti: visų trijų kintamųjų reikšmės, r
, g
ir b
, turi būti nuo 0 iki 255 imtinai. Be to, norint, kad šių kintamųjų spalva būtų tinkama, ji turi būti naujausia spalva, perduota konstruktoriui arba setColor ()
metodu arba pagaminta naudojant apversti ()
metodas.
Jei vienos gijos aplinkoje jūs naudojatės setColor ()
ir praeiti mėlyna spalva RGBSpalva
objektas bus mėlynas, kai setColor ()
grįžta. Jei tada pasikviesite „getColor“ ()
ant to paties objekto gausite mėlyną spalvą. Vienos gijos visuomenėje tai yra RGBSpalva
klasės yra gerai išauklėti.
Į darbus įmetė vienu metu esantį veržliaraktį
Deja, šis laimingas gerai išauklėtos nuotraukos vaizdas RGBSpalva
objektas gali tapti baisus, kai į paveikslėlį patenka kitos gijos. Daugialypėje aplinkoje RGBSpalva
aukščiau apibrėžta klasė yra linkusi į dviejų rūšių blogą elgesį: rašymo / rašymo konfliktai ir skaitymo / rašymo konfliktai.
Rašymo / rašymo konfliktai
Įsivaizduokite, kad turite dvi gijas, viena gija pavadinta „raudona“, kita - „mėlyna“. Abi gijos bando nustatyti to paties spalvą RGBSpalva
objektas: raudonas siūlas bando nustatyti raudoną spalvą; mėlynas siūlas bando nustatyti spalvą į mėlyną.
Abi šios gijos bando tuo pačiu metu rašyti to paties objekto egzemplioriaus kintamuosius. Jei gijų tvarkaraštis persipina šias dvi gijas teisingai, abi gijos netyčia trukdys viena kitai, sukeldamos rašymo / rašymo konfliktą. Proceso metu dvi gijos sugadins objekto būseną.
Nesinchronizuotas RGBSpalva
programėlė
Šis programėlė, pavadinta Nesinchronizuota RGB spalva, rodo vieną įvykių seką, kuri gali sukelti korupciją RGBSpalva
objektas. Raudonas siūlas nekaltai bando nustatyti raudoną spalvą, o mėlynas siūlas - nekaltą. Galų gale RGBSpalva
objektas nėra nei raudonas, nei mėlynas, bet nerimą kelianti spalva - purpurinė.
Peržengti įvykių, kurie veda prie sugadintų, seką RGBSpalva
objektą, paspauskite programėlės mygtuką „Step“. Paspauskite Atgal, jei norite sukurti atsarginę veiksmo kopiją, ir Atkurti, jei norite sukurti atsarginę kopiją iki pradžios. Einant programėlės apačioje esančioje teksto eilutėje bus paaiškinta, kas vyksta kiekvieno žingsnio metu.
Tiems iš jūsų, kurie negali paleisti programėlės, pateikiama lentelė, kurioje rodoma programėlės demonstruojama įvykių seka:
Siūlas | Pareiškimas | r | g | b | Spalva |
nė vienas | objektas reiškia žalią | 0 | 255 | 0 | |
mėlyna | mėlynas siūlas iškviečia setColor (0, 0, 255) | 0 | 255 | 0 | |
mėlyna | checkRGBVals (0, 0, 255); | 0 | 255 | 0 | |
mėlyna | tai.r = 0; | 0 | 255 | 0 | |
mėlyna | tai.g = 0; | 0 | 255 | 0 | |
mėlyna | mėlyna gauna iš anksto | 0 | 0 | 0 | |
raudona | raudonas siūlas iškviečia setColor (255, 0, 0) | 0 | 0 | 0 | |
raudona | checkRGBVals (255, 0, 0); | 0 | 0 | 0 | |
raudona | tai.r = 255; | 0 | 0 | 0 | |
raudona | tai.g = 0; | 255 | 0 | 0 | |
raudona | tai.b = 0; | 255 | 0 | 0 | |
raudona | grįžta raudonas siūlas | 255 | 0 | 0 | |
mėlyna | vėliau mėlynas siūlas tęsiasi | 255 | 0 | 0 | |
mėlyna | tai.b = 255 | 255 | 0 | 0 | |
mėlyna | grįžta mėlynas siūlas | 255 | 0 | 255 | |
nė vienas | objektas reprezentuoja purpurinę spalvą | 255 | 0 | 255 |
Kaip matote iš šios programėlės ir lentelės, RGBSpalva
yra sugadintas, nes gijų planavimo priemonė pertraukia mėlyną giją, kol objektas vis dar laikinai negaliojančios būsenos. Kai įeina raudonas siūlas ir nudažo objektą raudonai, mėlynas siūlas objektą nudažo tik iš dalies. Kai mėlynas siūlas grįžta baigti darbo, jis netyčia sugadina objektą.
Skaitymo / rašymo konfliktai
Kita netinkamo elgesio rūšis, kuri gali pasireikšti daugialypėje aplinkoje RGBSpalva
klasė yra skaitymo / rašymo konfliktai. Toks konfliktas kyla, kai objekto būsena yra skaitoma ir naudojama laikinai negaliojančioje būsenoje dėl nebaigto kitos gijos darbo.
Pavyzdžiui, atkreipkite dėmesį, kad vykdant mėlyną giją setColor ()
metodas, objektas vienu metu atsiduria laikinai negaliojančioje juodos spalvos būsenoje. Čia juoda spalva yra laikinai negaliojanti būsena, nes:
Tai laikina: galų gale mėlynas siūlas ketina nustatyti mėlyną spalvą.
- Tai neteisinga: niekas neprašė juodos spalvos
RGBSpalva
objektas. Mėlynas siūlas turėtų paversti žalią daiktą mėlynu.
Jei šiuo metu mėlynas siūlas yra apsaugotas, objektas vaizduoja juodą siūlu, kuris iškviečia „getColor“ ()
ant to paties objekto ta antroji gija stebėtų RGBSpalva
objekto vertė būti juoda.
Štai lentelė, kurioje pateikiama įvykių seka, dėl kurios gali kilti būtent toks skaitymo / rašymo konfliktas:
Siūlas | Pareiškimas | r | g | b | Spalva |
nė vienas | objektas reiškia žalią | 0 | 255 | 0 | |
mėlyna | mėlynas siūlas iškviečia setColor (0, 0, 255) | 0 | 255 | 0 | |
mėlyna | checkRGBVals (0, 0, 255); | 0 | 255 | 0 | |
mėlyna | tai.r = 0; | 0 | 255 | 0 | |
mėlyna | tai.g = 0; | 0 | 255 | 0 | |
mėlyna | mėlyna gauna iš anksto | 0 | 0 | 0 | |
raudona | raudona gija iškviečia getColor () | 0 | 0 | 0 | |
raudona | int [] retVal = naujas int [3]; | 0 | 0 | 0 | |
raudona | retVal [0] = 0; | 0 | 0 | 0 | |
raudona | retVal [1] = 0; | 0 | 0 | 0 | |
raudona | retVal [2] = 0; | 0 | 0 | 0 | |
raudona | grįžti retVal; | 0 | 0 | 0 | |
raudona | raudonas siūlas grąžina juodą | 0 | 0 | 0 | |
mėlyna | vėliau mėlynas siūlas tęsiasi | 0 | 0 | 0 | |
mėlyna | tai.b = 255 | 0 | 0 | 0 | |
mėlyna | grįžta mėlynas siūlas | 0 | 0 | 255 | |
nė vienas | objektas reiškia mėlyną | 0 | 0 | 255 |
Kaip matote iš šios lentelės, bėdos prasideda tada, kai mėlynas siūlas nutrūksta, kai jis tik iš dalies baigė mėlyną objektą. Šiuo metu objektas yra laikinai negaliojančios juodos būsenos, būtent tai mato raudonas siūlas, kai jis pasikviečia „getColor“ ()
ant objekto.
Trys būdai, kaip padaryti objektą saugų siūlams
Iš esmės yra trys būdai, kuriuos galite naudoti norėdami sukurti tokį objektą kaip RGBgija
saugus siūlams:
- Sinchronizuokite kritines dalis
- Padarykite tai nekintamą
- Naudokite vyniotinį, kuris yra saugus siūlams
1 požiūris: kritinių skyrių sinchronizavimas
Paprasčiausias būdas ištaisyti nepaklusnų elgesį, kurį demonstruoja tokie objektai kaip RGBSpalva
įterptas į daugialypį kontekstą, reikia sinchronizuoti kritines objekto dalis. Objekto kritiniai skyriai yra tie metodai arba kodų blokai metoduose, kuriuos vienu metu turi vykdyti tik viena gija. Kitaip tariant, kritinis skyrius yra metodas arba kodo blokas, kuris turi būti vykdomas atominiu būdu, kaip viena nedaloma operacija. Naudojant „Java“ sinchronizuotas
raktinį žodį, galite garantuoti, kad tik viena gija vienu metu vykdys kritines objekto dalis.
Norėdami laikytis tokio požiūrio, kad jūsų objektas būtų saugus, turite atlikti du veiksmus: visus susijusius laukus turite padaryti privačius, taip pat turite identifikuoti ir sinchronizuoti visus kritinius skyrius.
1 veiksmas: laukus paverskite privačiais
Sinchronizavimas reiškia, kad tik viena gija vienu metu galės vykdyti šiek tiek kodo (kritinis skyrius). Taigi, nors taip ir yra laukai norite koordinuoti prieigą prie kelių gijų, „Java“ mechanizmas tai padaryti iš tikrųjų koordinuoja prieigą prie kodas. Tai reiškia, kad tik padarę duomenis privačius, galėsite kontroliuoti prieigą prie tų duomenų, kontroliuodami prieigą prie kodo, kuris manipuliuoja duomenimis.