Programovanie

Dizajn pre bezpečnosť závitov

Pred šiestimi mesiacmi som začal sériu článkov o navrhovaní tried a objektov. V tomto mesiaci Dizajnové techniky V tomto stĺpci budem pokračovať v sledovaní princípov návrhu, ktoré sa týkajú bezpečnosti závitov. Tento článok vám povie, čo je bezpečnosť nití, prečo ich potrebujete, kedy ich potrebujete a ako ich získať.

Čo je to bezpečnosť nití?

Bezpečnosť vlákien jednoducho znamená, že polia objektu alebo triedy si vždy zachovávajú platný stav, ako to pozorujú iné objekty a triedy, aj keď sú súčasne využívané viacerými vláknami.

Jedným z prvých pokynov, ktoré som v tomto stĺpci navrhol (pozri „Návrh inicializácie objektu“), je, že by ste mali navrhovať triedy tak, aby si objekty udržiavali platný stav od začiatku svojej životnosti až do konca. Ak budete postupovať podľa tejto rady a vytvoríte objekty, ktorých premenné inštancie sú všetky súkromné ​​a ktorých metódy umožňujú iba správne prechody stavu v týchto premenných inštancie, ste v dobrej kondícii v prostredí s jedným vláknom. Keď sa však vyskytnú ďalšie vlákna, môžete sa dostať do problémov.

Viaceré vlákna môžu znamenať pre váš objekt problémy, pretože často môže byť stav objektu počas vykonávania metódy dočasne neplatný. Ak objekt vyvoláva iba jedno vlákno, vykoná sa vždy iba jedna metóda a každá sa nechá dokončiť pred vyvolaním inej metódy. V prostredí s jedným vláknom bude mať teda každá metóda šancu zabezpečiť, aby sa akýkoľvek dočasne neplatný stav zmenil na platný predtým, ako sa metóda vráti.

Po zavedení viacerých vlákien však môže JVM prerušiť vlákno vykonávajúce jednu metódu, zatiaľ čo premenné inštancie objektu sú stále v dočasne neplatnom stave. Spoločný podnik JVM potom mohol dať šancu vykonať inému vláknu a toto vlákno mohlo zavolať metódu na rovnakom objekte. Celá vaša tvrdá práca na tom, aby boli vaše inštančné premenné súkromné ​​a vaše metódy vykonávali iba platné transformácie stavu, nebude stačiť na to, aby zabránila tomuto druhému vláknu v pozorovaní objektu v neplatnom stave.

Takýto objekt by nebol bezpečný pre vlákna, pretože vo viacvláknovom prostredí by sa objekt mohol poškodiť alebo by sa mohlo stať, že bude mať neplatný stav. Objekt bezpečný pre vlákna je objekt, ktorý si vždy zachováva platný stav, ako ho pozorujú iné triedy a objekty, a to aj v prostredí s viacerými vláknami.

Prečo sa obávať o bezpečnosť nití?

Pri navrhovaní tried a objektov v Jave musíte myslieť na bezpečnosť vlákien z dvoch veľkých dôvodov:

  1. Podpora viacerých vlákien je zabudovaná do jazyka Java a API

  2. Všetky vlákna vo virtuálnom stroji Java (JVM) zdieľajú rovnakú haldu a oblasť metód

Pretože multithreading je zabudovaný do Javy, je možné, že ktorákoľvek trieda, ktorú nakoniec navrhnete, môže byť súčasne použitá viacerými vláknami. Nemusíte (a nemali by ste) robiť každú triedu, ktorú navrhujete, bezpečnou pre vlákna, pretože bezpečnosť pre vlákna neprichádza zadarmo. Mali by ste však minimálne myslieť si o bezpečnosti vlákien pri každom návrhu triedy Java. Ďalej v tomto článku nájdete diskusiu o nákladoch na bezpečnosť vlákien a pokyny týkajúce sa toho, kedy je treba zaistiť bezpečnú triedu pre vlákna.

Vzhľadom na architektúru JVM sa musíte starať iba o premenné inštancie a triedy, keď sa obávate o bezpečnosť vlákien. Pretože všetky vlákna zdieľajú rovnakú haldu a v halde sú uložené všetky premenné inštancie, môže sa viac vlákien pokúsiť súčasne použiť inštančné premenné toho istého objektu. Rovnako tak, pretože všetky vlákna zdieľajú rovnakú oblasť metódy a v oblasti metódy sú uložené všetky premenné triedy, viaceré vlákna sa môžu pokúsiť súčasne použiť rovnaké premenné triedy. Ak sa rozhodnete zaistiť bezpečnosť triedy s vláknami, vaším cieľom je zaručiť integritu - v prostredí s viacerými vláknami - premenných inštancie a triedy deklarovaných v tejto triede.

Nemusíte sa obávať viacvláknového prístupu k lokálnym premenným, parametrom metódy a návratovým hodnotám, pretože tieto premenné sa nachádzajú v zásobníku Java. V JVM má každé vlákno pridelený svoj vlastný zásobník Java. Žiadne vlákno nemôže vidieť ani používať žiadne lokálne premenné, návratové hodnoty alebo parametre patriace k inému vláknu.

Vzhľadom na štruktúru JVM sú miestne premenné, parametre metódy a návratové hodnoty vo svojej podstate „bezpečné pre vlákna“. Premenné inštancie a premenné triedy však budú bezpečné z hľadiska vlákien, iba ak svoju triedu navrhnete vhodným spôsobom.

RGBColor # 1: Pripravené pre jedno vlákno

Ako príklad triedy, ktorá je nie bezpečné pre vlákna, zvážte RGBColor triedy, zobrazené nižšie. Inštancie tejto triedy predstavujú farbu uloženú v troch premenných súkromnej inštancie: r, ga b. Vzhľadom na triedu uvedenú nižšie, RGBColor objekt by začal svoj život v platnom stave a zažil by iba prechody platného stavu od začiatku svojho života do konca - ale iba v prostredí s jedným vláknom.

// V súborových vláknach / ex1 / RGBColor.java // Inštancie tejto triedy NIE sú bezpečné pre vlákna. verejná trieda RGBColor {private int r; súkromný int g; súkromný int b; public RGBColor (int r, int g, int b) {checkRGBVals (r, g, b); this.r = r; this.g = g; this.b = b; } public void setColor (int r, int g, int b) {checkRGBVals (r, g, b); this.r = r; this.g = g; this.b = b; } / ** * vracia farbu v poli troch ints: R, G a B * / public int [] getColor () {int [] retVal = new int [3]; retVal [0] = r; retVal [1] = g; retVal [2] = b; návrat retVal; } public void invert () {r = 255 - r; g = 255 - g; b = 255 - b; } private static void checkRGBVals (int r, int g, int b) {if (r 255 || g 255 || b <0 || b> 255) {throw new IllegalArgumentException (); }}} 

Pretože tri inštančné premenné, ints r, ga b, sú súkromné, jediný spôsob, ako môžu iné triedy a objekty pristupovať alebo ovplyvňovať hodnoty týchto premenných, je cez RGBColorkonštruktor a metódy. Dizajn konštruktéra a metódy zaručujú, že:

  1. RGBColorKonštruktér dá premenným vždy správne počiatočné hodnoty

  2. Metódy setColor () a invertovať () vždy vykoná platné transformácie stavu na týchto premenných

  3. Metóda getColor () vždy vráti platné zobrazenie týchto premenných

Upozorňujeme, že ak sa zlé údaje odovzdajú konštruktérovi alebo setColor () metódou, ukončia náhle s InvalidArgumentException. The checkRGBVals () metóda, ktorá vrhá túto výnimku, v skutočnosti definuje, čo to znamená pre RGBColor objekt, ktorý má byť platný: hodnoty všetkých troch premenných, r, ga b, musí byť medzi 0 a 255 vrátane. Aby bola platná, farba reprezentovaná týmito premennými musí byť navyše najnovšou farbou odovzdanou konštruktoru alebo setColor () metódou alebo vyrobenou invertovať () metóda.

Ak v prostredí s jedným vláknom vyvoláte setColor () a odovzdať modrou farbou RGBColor objekt bude modrý, keď setColor () vracia. Ak sa potom dovoláte getColor () na rovnakom objekte dostanete modrú farbu. V spoločnosti s jedným vláknom to sú prípady RGBColor triedy sú slušne vychovaní.

Vhadzovanie súbežného kľúča do diel

Bohužiaľ, tento šťastný obraz dobre vychovaného RGBColor objekt môže byť strašidelný, keď do obrazu vstúpia ďalšie vlákna. Vo viacvláknovom prostredí sú inštancie RGBColor triedy definované vyššie sú náchylné na dva druhy zlého správania: konflikty zápisu / zápisu a konflikty čítania / zápisu.

Konflikty zápisu / zápisu

Predstavte si, že máte dve vlákna, jedno vlákno s názvom „červené“ a ďalšie s názvom „modré“. Obe vlákna sa snažia nastaviť rovnakú farbu RGBColor objekt: Červená niť sa pokúša nastaviť farbu na červenú; modrá niť sa pokúša nastaviť farbu na modrú.

Obe tieto vlákna sa pokúšajú súčasne zapisovať do inštančných premenných toho istého objektu. Ak plánovač vlákien prekladá tieto dve vlákna správnym spôsobom, tieto dve vlákna sa nechtiac navzájom ovplyvnia, čo spôsobí konflikt zápisu a zápisu. V tomto procese dve vlákna poškodia stav objektu.

The Nesynchronizované RGBColor applet

Nasledujúci applet s názvom Nesynchronizovaná farba RGBColor, demonštruje jednu postupnosť udalostí, ktoré by mohli viesť k poškodeniu RGBColor objekt. Červená niť sa nevinne snaží nastaviť farbu na červenú, zatiaľ čo modrá niť sa nevinne snaží nastaviť farbu na modrú. Nakoniec RGBColor objekt nepredstavuje ani červenú, ani modrú farbu, ale znepokojujúcu farbu, purpurovú.

Prehliadač vám z nejakého dôvodu nedovolí vidieť tento úžasný applet Java.

Postupovať postupnosťou udalostí, ktoré vedú k poškodeniu RGBColor objektu, stlačte tlačidlo Krok na applete. Stlačením Späť zálohujete krok a obnovením Obnovíte zálohovanie na začiatok. Postupne bude riadok textu v dolnej časti appletu vysvetľovať, čo sa deje počas každého kroku.

Pre tých z vás, ktorí nemôžu spustiť applet, je tu tabuľka, ktorá zobrazuje postupnosť udalostí demonštrovaných appletom:

ZávitVyhláseniergbFarba
žiadnyobjekt predstavuje zelenú farbu02550 
Modrámodré vlákno vyvolá setColor (0, 0, 255)02550 
ModrácheckRGBVals (0, 0, 255);02550 
Modráthis.r = 0;02550 
Modráthis.g = 0;02550 
Modrámodrá dostane prednosť000 
červenáčervené vlákno vyvoláva setColor (255, 0, 0)000 
červenácheckRGBVals (255, 0, 0);000 
červenáthis.r = 255;000 
červenáthis.g = 0;25500 
červenáthis.b = 0;25500 
červenáčervená niť sa vráti25500 
Modráneskôr modrá niť pokračuje25500 
Modráthis.b = 25525500 
Modrámodrá niť sa vráti2550255 
žiadnyobjekt predstavuje purpurovú farbu2550255 

Ako vidíte z tohto appletu a tabuľky, RGBColor je poškodený, pretože plánovač vlákien preruší modré vlákno, kým je objekt stále v dočasne neplatnom stave. Keď vstúpi červená niť a zafarbí objekt na červeno, modrá niť je farbou natretá iba čiastočne. Keď sa modré vlákno vráti a dokončí prácu, neúmyselne poškodí objekt.

Konflikty čítania a zápisu

Iný druh nesprávneho správania, ktorý sa môže vyskytnúť v prostredí s viacerými vláknami RGBColor triedy sú konflikty čítania a zápisu. Tento druh konfliktu vzniká, keď je stav objektu načítaný a používaný v dočasne neplatnom stave z dôvodu nedokončenej práce iného vlákna.

Napríklad si všimnite, že počas vykonávania modrej nite setColor () vyššie, objekt sa v jednom okamihu ocitne v dočasne neplatnom stave čiernej. Čierna je tu dočasne neplatný stav, pretože:

  1. Je to dočasné: Nakoniec má modrá niť v úmysle nastaviť farbu na modrú.

  2. Je neplatné: Nikto nepožiadal o čiernu farbu RGBColor objekt. Modrá niť má zmeniť zelený predmet na modrý.

Ak je modré vlákno v danom okamihu vopred zabránené, objekt predstavuje čierne vlákno, ktoré vyvoláva getColor () na rovnakom objekte by toto druhé vlákno sledovalo RGBColor hodnota objektu bude čierna.

Tu je tabuľka, ktorá zobrazuje postupnosť udalostí, ktoré by mohli viesť práve k takémuto konfliktu čítania a zápisu:

ZávitVyhláseniergbFarba
žiadnyobjekt predstavuje zelenú farbu02550 
Modrámodré vlákno vyvolá setColor (0, 0, 255)02550 
ModrácheckRGBVals (0, 0, 255);02550 
Modráthis.r = 0;02550 
Modráthis.g = 0;02550 
Modrámodrá dostane prednosť000 
červenáčervené vlákno vyvoláva getColor ()000 
červenáint [] retVal = nový int [3];000 
červenáretVal [0] = 0;000 
červenáretVal [1] = 0;000 
červenáretVal [2] = 0;000 
červenánávrat retVal;000 
červenáčervená niť sa vracia čierna000 
Modráneskôr modrá niť pokračuje000 
Modráthis.b = 255000 
Modrámodrá niť sa vráti00255 
žiadnyobjekt predstavuje modrú farbu00255 

Ako môžete vidieť z tejto tabuľky, problém začína, keď sa modrá niť preruší, keď objekt natrie na modro iba čiastočne. V tomto okamihu je objekt v dočasne neplatnom stave čiernej farby, čo presne vidí červená niť, keď sa vyvolá getColor () na objekte.

Tri spôsoby, ako zaistiť bezpečnosť objektu voči vláknam

V zásade existujú tri prístupy, ktoré môžete urobiť pri výrobe objektu, ako je napr RGBThread bezpečné pre vlákna:

  1. Synchronizujte kritické sekcie
  2. Nech je to nemenné
  3. Použite obal bezpečný pre nite

Prístup 1: Synchronizácia kritických častí

Najpriamočiarejší spôsob nápravy neposlušného správania objektov, ako sú napr RGBColor keď je umiestnený vo viacvláknovom kontexte, má sa synchronizovať kritická časť objektu. Objekt kritické úseky sú tie metódy alebo bloky kódu v rámci metód, ktoré musia byť súčasne vykonávané iba jedným vláknom. Inými slovami, kritickou časťou je metóda alebo blok kódu, ktorý sa musí vykonať atómovo ako jedna nedeliteľná operácia. Použitím Java synchronizované kľúčové slovo, môžete zaručiť, že kritické sekcie objektu vykoná vždy iba jedno vlákno naraz.

Ak chcete použiť tento prístup k zaisteniu bezpečnosti svojho objektu s vláknami, musíte postupovať podľa dvoch krokov: musíte všetky príslušné polia označiť ako súkromné ​​a všetky kritické sekcie musíte identifikovať a synchronizovať.

Krok 1: Pole označte ako súkromné

Synchronizácia znamená, že iba jedno vlákno naraz bude schopné vykonať kúsok kódu (kritická časť). Takže aj keď je polia ak chcete koordinovať prístup medzi viacerými vláknami, mechanizmus Java to skutočne koordinuje prístup k kód. To znamená, že iba ak údaje označíte ako súkromné, budete môcť riadiť prístup k týmto údajom riadením prístupu ku kódu, ktorý s údajmi manipuluje.

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