Programavimas

Sriegių saugumo dizainas

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ų:

  1. Kelių gijų palaikymas yra integruotas į „Java“ kalbą ir API

  2. 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, gir 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, tarpts r, gir b, yra privatūs, vienintelis būdas, kuriuo kitos klasės ir objektai gali pasiekti ar paveikti šių kintamųjų reikšmes, yra RGBSpalvakonstruktorius ir metodai. Konstruktoriaus dizainas ir metodai garantuoja, kad:

  1. RGBSpalvakonstruktorius visada duos kintamiesiems tinkamas pradines reikšmes

  2. Metodai setColor () ir apversti () visada atliks galiojančias šių kintamųjų būsenos transformacijas

  3. 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, gir 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ė.

Dėl tam tikrų priežasčių jūsų naršyklė neleis jums taip matyti šaunios „Java“ programėlės.

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ūlasPareiškimasrgbSpalva
nė vienasobjektas reiškia žalią02550 
mėlynamėlynas siūlas iškviečia setColor (0, 0, 255)02550 
mėlynacheckRGBVals (0, 0, 255);02550 
mėlynatai.r = 0;02550 
mėlynatai.g = 0;02550 
mėlynamėlyna gauna iš anksto000 
raudonaraudonas siūlas iškviečia setColor (255, 0, 0)000 
raudonacheckRGBVals (255, 0, 0);000 
raudonatai.r = 255;000 
raudonatai.g = 0;25500 
raudonatai.b = 0;25500 
raudonagrįžta raudonas siūlas25500 
mėlynavėliau mėlynas siūlas tęsiasi25500 
mėlynatai.b = 25525500 
mėlynagrįžta mėlynas siūlas2550255 
nė vienasobjektas reprezentuoja purpurinę spalvą2550255 

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:

  1. Tai laikina: galų gale mėlynas siūlas ketina nustatyti mėlyną spalvą.

  2. 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ūlasPareiškimasrgbSpalva
nė vienasobjektas reiškia žalią02550 
mėlynamėlynas siūlas iškviečia setColor (0, 0, 255)02550 
mėlynacheckRGBVals (0, 0, 255);02550 
mėlynatai.r = 0;02550 
mėlynatai.g = 0;02550 
mėlynamėlyna gauna iš anksto000 
raudonaraudona gija iškviečia getColor ()000 
raudonaint [] retVal = naujas int [3];000 
raudonaretVal [0] = 0;000 
raudonaretVal [1] = 0;000 
raudonaretVal [2] = 0;000 
raudonagrįžti retVal;000 
raudonaraudonas siūlas grąžina juodą000 
mėlynavėliau mėlynas siūlas tęsiasi000 
mėlynatai.b = 255000 
mėlynagrįžta mėlynas siūlas00255 
nė vienasobjektas reiškia mėlyną00255 

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:

  1. Sinchronizuokite kritines dalis
  2. Padarykite tai nekintamą
  3. 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.