Programmēšana

Dizains vītņu drošībai

Pirms sešiem mēnešiem es sāku rakstu sēriju par nodarbību un priekšmetu dizainu. Šajā mēnesī Dizaina paņēmieni slejā, es turpināšu šo sēriju, aplūkojot dizaina principus, kas attiecas uz vītņu drošību. Šajā rakstā ir pastāstīts, kas ir diegu drošība, kāpēc tā jums ir nepieciešama, kad jums tā nepieciešama un kā to iegūt.

Kas ir vītņu drošība?

Vītņu drošība vienkārši nozīmē, ka objekta vai klases lauki vienmēr saglabā derīgu stāvokli, kā to novēro citi objekti un klases, pat ja tos vienlaikus lieto vairāki pavedieni.

Viena no pirmajām vadlīnijām, ko es piedāvāju šajā slejā (sk. "Objektu inicializācijas projektēšana"), ir tas, ka jums vajadzētu veidot klases tā, lai objekti saglabātu derīgu stāvokli, sākot no to dzīves sākuma līdz beigām. Ja sekojat šim ieteikumam un izveidojat objektus, kuru instances mainīgie visi ir privāti un kuru metodes tikai veic pareizu stāvokļa pāreju uz šiem instances mainīgajiem, jūs esat labā formā ar vienu pavedienu veidotu vidi. Bet jūs varat nonākt nepatikšanās, kad rodas vairāk pavedienu.

Vairāki pavedieni var radīt problēmas jūsu objektam, jo ​​bieži vien, kamēr metode tiek izpildīta, objekta stāvoklis var būt īslaicīgi nederīgs. Kad tikai viena pavediens atsaucas uz objekta metodēm, vienlaikus tiks izpildīta tikai viena metode, un katrai metodei tiks atļauts pabeigt pirms citas metodes izsaukšanas. Tādējādi vienā pavedienā vidē katrai metodei tiks dota iespēja pārliecināties, vai jebkurš īslaicīgi nederīgs stāvoklis tiek mainīts par derīgu, pirms metode atgriežas.

Pēc vairāku pavedienu ieviešanas JVM var pārtraukt pavedienu, izpildot vienu metodi, kamēr objekta instances mainīgie joprojām ir īslaicīgi nederīgā stāvoklī. Pēc tam JVM varēja dot iespēju citam pavedienam izpildīt, un šī pavediens varētu izsaukt metodi tam pašam objektam. Ar visu jūsu smago darbu, lai jūsu instances mainīgie būtu privāti, un jūsu metodes veic tikai derīgas stāvokļa transformācijas, nepietiks, lai neļautu šai otrajai pavedienai novērot objektu nederīgā stāvoklī.

Šāds objekts nebūtu drošs ar pavedieniem, jo ​​vairāku pavedienu vidē objekts var tikt bojāts vai tiek novērots nederīgs stāvoklis. Vītnei drošs objekts ir tāds, kas vienmēr uztur derīgu stāvokli, kā to novēro citas klases un objekti, pat daudzvītņu vidē.

Kāpēc jāuztraucas par vītņu drošību?

Veidojot klases un objektus Java, jums ir jādomā par pavedienu drošību diviem galvenajiem iemesliem:

  1. Vairāku pavedienu atbalsts ir iebūvēts Java valodā un API

  2. Visiem Java virtuālās mašīnas (JVM) iekšējiem pavedieniem ir viens un tas pats kaudzes un metodes apgabals

Tā kā Java ir iebūvēta daudzsavienošana, ir iespējams, ka jebkuru jūsu izstrādāto klasi var vienlaikus izmantot vairāki pavedieni. Jums nav jāpadara (un nevajadzētu) padarīt katru klasi, kuru veidojat, ar vītni, jo diegu drošība nenāk par velti. Bet jums vajadzētu vismaz padomā par pavedienu drošību katru reizi, kad projektējat Java klasi. Šajā rakstā vēlāk atradīsit diskusiju par diegu drošības izmaksām un vadlīnijas par to, kad klasēm jāpadara drošas.

Ņemot vērā JVM arhitektūru, jums jāuztraucas par instances un klases mainīgajiem tikai tad, kad uztraucaties par pavedienu drošību. Tā kā visiem pavedieniem ir viens un tas pats kaudze, un kaudzē atrodas visi instances mainīgie, vairāki pavedieni var mēģināt vienlaicīgi izmantot viena un tā paša objekta instances mainīgos. Tāpat kā tāpēc, ka visiem pavedieniem ir viena un tā pati metode, un metodes apgabalā tiek glabāti visi klases mainīgie, vairāki pavedieni var mēģināt vienlaikus izmantot tos pašus klases mainīgos. Izvēloties padarīt klases pavedienu drošu, jūsu mērķis ir garantēt šajā klasē deklarēto piemēru un klases mainīgo integritāti daudzlīniju vidē.

Jums nav jāuztraucas par daudzlīniju piekļuvi vietējiem mainīgajiem, metožu parametriem un atgriešanās vērtībām, jo ​​šie mainīgie atrodas Java kaudzē. JVM katram pavedienam tiek piešķirta sava Java kaudze. Neviens pavediens nevar redzēt vai izmantot vietējos mainīgos, atgriešanās vērtības vai parametrus, kas pieder citai pavedienam.

Ņemot vērā JVM struktūru, vietējie mainīgie, metožu parametri un atgriešanās vērtības pēc būtības ir “drošas ar pavedieniem”. Bet gadījumu mainīgie un klases mainīgie būs droši tikai ar pavedieniem, ja jūs atbilstoši noformēsit savu klasi.

RGBColor # 1: gatavs vienam pavedienam

Kā klases piemērs, kas ir drošs ar vītni, apsveriet RGB krāsa klase, parādīta zemāk. Šīs klases gadījumi attēlo krāsu, kas saglabāta trīs privāto instanču mainīgajos: r, g, un b. Ņemot vērā zemāk redzamo klasi, an RGB krāsa objekts sāktu savu dzīvi derīgā stāvoklī un piedzīvotu tikai derīgu stāvokļu pārejas, sākot no tā dzīves sākuma līdz beigām - bet tikai ar vienu pavedienu vidē.

// Failu pavedienos / ex1 / RGBColor.java // Šīs klases gadījumi NAV droši ar pavedieniem. publiskā klase RGBColor {private int r; privāts int g; privāts int b; public RGBColor (int r, int g, int b) {checkRGBVals (r, g, b); this.r = r; tas.g = g; šī.b = b; } public void setColor (int r, int g, int b) {checkRGBVals (r, g, b); this.r = r; tas.g = g; šī.b = b; } / ** * atgriež krāsu trīs intu masīvā: R, G un B * / public int [] getColor () {int [] retVal = new int [3]; retVal [0] = r; retVal [1] = g; retVal [2] = b; atgriezties 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) {mest jaunu IllegalArgumentException (); }}} 

Tā kā trīs gadījumu mainīgie, ints r, g, un b, ir privāti, vienīgais veids, kā citas klases un objekti var piekļūt vai ietekmēt šo mainīgo vērtības, ir RGB krāsakonstruktors un metodes. Konstruktora un metožu dizains garantē, ka:

  1. RGB krāsakonstruktors vienmēr piešķirs mainīgajiem pareizas sākotnējās vērtības

  2. Metodes setColor () un apgriezt () vienmēr veiks derīgas stāvokļa transformācijas šiem mainīgajiem

  3. Metode getColor () vienmēr atgriezīs derīgu šo mainīgo skatu

Ņemiet vērā, ka, ja slikti dati tiek nodoti konstruktoram vai setColor () metodi, tie pēkšņi tiks pabeigti ar InvalidArgumentException. The checkRGBVals () metode, kas izsaka šo izņēmumu, faktiski nosaka, ko tā nozīmē RGB krāsa objekts ir derīgs: visu trīs mainīgo vērtības, r, g, un b, jābūt starp 0 un 255 ieskaitot. Turklāt, lai šo mainīgo krāsa būtu derīga, tai jābūt jaunākajai krāsai, kas vai nu nodota konstruktoram, vai arī setColor () metodi vai ražo apgriezt () metodi.

Ja jūs izmantojat vienu pavedienu vidē setColor () un iet zilā krāsā RGB krāsa objekts būs zils, kad setColor () atgriežas. Ja jūs pēc tam piesaucat getColor () uz tā paša objekta jūs iegūsiet zilu krāsu. Viena pavediena sabiedrībā tas notiek RGB krāsa klases ir labi izturējušies.

Darbos iemetot vienlaicīgu uzgriežņu atslēgu

Diemžēl šī priecīgā bilde par labi izturējušos RGB krāsa objekts var kļūt biedējošs, kad attēlā ienāk citi pavedieni. Vairāku pavedienu vidē RGB krāsa iepriekš definētā klase ir pakļauta divu veidu sliktai uzvedībai: rakstīšanas / rakstīšanas konflikti un lasīšanas / rakstīšanas konflikti.

Rakstīšanas / rakstīšanas konflikti

Iedomājieties, ka jums ir divi pavedieni, viens pavediens ar nosaukumu "sarkans" un otrs ar nosaukumu "zils". Abi pavedieni mēģina iestatīt to pašu krāsu RGB krāsa objekts: sarkanais pavediens mēģina iestatīt krāsu uz sarkanu; zils pavediens mēģina iestatīt zilu krāsu.

Abas šīs pavedieni vienlaikus mēģina rakstīt viena un tā paša objekta instances mainīgajiem. Ja pavedienu plānotājs šos divus pavedienus sapludina pareizajā veidā, abi pavedieni netīšām traucēs viens otru, radot rakstīšanas / rakstīšanas konfliktu. Šajā procesā abi pavedieni sabojā objekta stāvokli.

The Nesinhronizēts RGB krāsa sīklietotne

Šis sīklietotne ar nosaukumu Nesinhronizēta RGB krāsa, parāda vienu notikumu secību, kas var izraisīt korumpētus RGB krāsa objekts. Sarkanais pavediens nevainīgi mēģina iestatīt krāsu uz sarkanu, bet zils pavediens nevainīgi mēģina iestatīt krāsu uz zilu. Galu galā RGB krāsa objekts attēlo ne sarkanu, ne zilu krāsu, bet nemierinošu krāsu - fuksīna.

Kādu iemeslu dēļ jūsu pārlūkprogramma neļaus jums šādā veidā redzēt foršu Java sīklietotni.

Lai pārietu uz notikumu secību, kas noved pie sabojāta RGB krāsa objektu, nospiediet sīklietotnes pogu Step. Nospiediet Atpakaļ, lai dublētu darbību, un Atiestatīt, lai dublētu sākumu. Ejot, teksta rindiņa sīklietotnes apakšā paskaidros, kas notiek katrā solī.

Tiem no jums, kuri nevar palaist sīklietotni, šeit ir tabula, kurā parādīta sīklietotnes demonstrētā notikumu secība:

VītnePaziņojums, apgalvojumsrgbKrāsa
nevienaobjekts apzīmē zaļu02550 
zilszils pavediens izsauc setColor (0, 0, 255)02550 
zilscheckRGBVals (0, 0, 255);02550 
zilsšī.r = 0;02550 
zilstas.g = 0;02550 
zilszils tiek novērsts000 
sarkanssarkans pavediens izsauc setColor (255, 0, 0)000 
sarkanscheckRGBVals (255, 0, 0);000 
sarkansšī.r = 255;000 
sarkanstas.g = 0;25500 
sarkansšī.b = 0;25500 
sarkansatgriežas sarkanais pavediens25500 
zilsvēlāk zils pavediens turpinās25500 
zilstas.b = 25525500 
zilszils pavediens atgriežas2550255 
nevienaobjekts attēlo purpursarkanu2550255 

Kā redzat no šīs sīklietotnes un tabulas, RGB krāsa ir bojāts, jo pavedienu plānotājs pārtrauc zilo pavedienu, kamēr objekts joprojām ir īslaicīgi nederīgs. Kad ienāk sarkanais pavediens un krāso objektu sarkanā krāsā, zilais pavediens ir tikai daļēji pabeigts, krāsojot objektu zilā krāsā. Kad zilais pavediens atgriežas, lai pabeigtu darbu, tas netīši sabojā objektu.

Lasīšanas / rakstīšanas konflikti

Cits nepareizas izturēšanās veids, ko var piemeklēt vairāku pavedienu vidē RGB krāsa klase ir lasīšanas / rakstīšanas konflikti. Šāda veida konflikts rodas, ja objekta stāvoklis tiek lasīts un izmantots, kamēr tas ir īslaicīgi nederīgs citas pavediena nepabeigta darba dēļ.

Piemēram, ņemiet vērā, ka zilā pavediena izpildes laikā setColor () metodi, objekts vienā brīdī nonāk īslaicīgi nederīgā melnā stāvoklī. Šeit melns ir īslaicīgi nederīgs stāvoklis, jo:

  1. Tas ir īslaicīgs: galu galā zilā pavediena mērķis ir iestatīt krāsu uz zilu.

  2. Tas nav derīgs: melnu krāsu neviens neprasīja RGB krāsa objekts. Paredzams, ka zilais pavediens pārvērš zaļo objektu zilā krāsā.

Ja zilā vītne šobrīd ir novērsta, objekts attēlo melno ar pavedienu, kas izsauc getColor () uz tā paša objekta šī otrā vītne novērotu RGB krāsa objekta vērtība ir melna.

Šeit ir tabula, kurā parādīta notikumu secība, kas var izraisīt tieši šādu lasīšanas / rakstīšanas konfliktu:

VītnePaziņojums, apgalvojumsrgbKrāsa
nevienaobjekts apzīmē zaļu02550 
zilszils pavediens izsauc setColor (0, 0, 255)02550 
zilscheckRGBVals (0, 0, 255);02550 
zilsšī.r = 0;02550 
zilstas.g = 0;02550 
zilszils tiek novērsts000 
sarkanssarkanais pavediens izsauc getColor ()000 
sarkansint [] retVal = jauns int [3];000 
sarkansretVal [0] = 0;000 
sarkansretVal [1] = 0;000 
sarkansretVal [2] = 0;000 
sarkansatgriezties retVal;000 
sarkanssarkans pavediens atgriež melnu000 
zilsvēlāk zils pavediens turpinās000 
zilstas.b = 255000 
zilszils pavediens atgriežas00255 
nevienaobjekts apzīmē zilu00255 

Kā redzams no šīs tabulas, nepatikšanas sākas, kad zilais pavediens tiek pārtraukts, kad tas objektu ir tikai daļēji pabeidzis krāsot zilā krāsā. Šajā brīdī objekts atrodas īslaicīgi nederīgā melnā stāvoklī, tieši to redz sarkanais pavediens, kad tas izsauc getColor () uz objekta.

Trīs veidi, kā padarīt objektu drošu

Lai izveidotu objektu, piemēram, var izmantot trīs pieejas RGBThread drošs ar vītni:

  1. Sinhronizēt kritiskās sadaļas
  2. Padariet to nemainīgu
  3. Izmantojiet iesaiņojumu, kas ir drošs ar vītni

1. pieeja: kritisko sadaļu sinhronizēšana

Visvienkāršākais veids, kā izlabot nepaklausīgo rīcību, ko demonstrē tādi objekti kā RGB krāsa ievietojot vairāku pavedienu kontekstā, ir sinhronizēt objekta kritiskās sadaļas. Objekta kritiskās sadaļas ir tās metodes vai kodu bloki metodēs, kuras vienlaikus jāizpilda tikai vienam pavedienam. Citiem vārdiem sakot, kritiskā sadaļa ir metode vai koda bloks, kas jāveic atomiski kā viena nedalāma operācija. Izmantojot Java sinhronizēts atslēgvārdu, jūs varat garantēt, ka tikai viena pavediens vienlaikus izpildīs objekta kritiskās sadaļas.

Lai izmantotu šo pieeju, lai objektu padarītu par drošu, jums jāveic divas darbības: visi attiecīgie lauki ir jāpadara privāti, kā arī jāidentificē un jāsinhronizē visas kritiskās sadaļas.

1. darbība: padariet laukus par privātiem

Sinhronizācija nozīmē, ka tikai viens pavediens vienlaikus varēs izpildīt mazliet koda (kritiskā sadaļa). Tātad, kaut arī tā ir lauki vēlaties koordinēt piekļuvi vairākiem pavedieniem, Java mehānisms, lai to izdarītu, faktiski koordinē piekļuvi kods. Tas nozīmē, ka tikai tad, ja padarīsit datus privātus, varēsit kontrolēt piekļuvi šiem datiem, kontrolējot piekļuvi kodam, kas manipulē ar datiem.

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