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:
Vairāku pavedienu atbalsts ir iebūvēts Java valodā un API
- 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 nē 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, int
s 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āsa
konstruktors un metodes. Konstruktora un metožu dizains garantē, ka:
RGB krāsa
konstruktors vienmēr piešķirs mainīgajiem pareizas sākotnējās vērtībasMetodes
setColor ()
unapgriezt ()
vienmēr veiks derīgas stāvokļa transformācijas šiem mainīgajiem- 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.
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ītne | Paziņojums, apgalvojums | r | g | b | Krāsa |
neviena | objekts apzīmē zaļu | 0 | 255 | 0 | |
zils | zils pavediens izsauc setColor (0, 0, 255) | 0 | 255 | 0 | |
zils | checkRGBVals (0, 0, 255); | 0 | 255 | 0 | |
zils | šī.r = 0; | 0 | 255 | 0 | |
zils | tas.g = 0; | 0 | 255 | 0 | |
zils | zils tiek novērsts | 0 | 0 | 0 | |
sarkans | sarkans pavediens izsauc setColor (255, 0, 0) | 0 | 0 | 0 | |
sarkans | checkRGBVals (255, 0, 0); | 0 | 0 | 0 | |
sarkans | šī.r = 255; | 0 | 0 | 0 | |
sarkans | tas.g = 0; | 255 | 0 | 0 | |
sarkans | šī.b = 0; | 255 | 0 | 0 | |
sarkans | atgriežas sarkanais pavediens | 255 | 0 | 0 | |
zils | vēlāk zils pavediens turpinās | 255 | 0 | 0 | |
zils | tas.b = 255 | 255 | 0 | 0 | |
zils | zils pavediens atgriežas | 255 | 0 | 255 | |
neviena | objekts attēlo purpursarkanu | 255 | 0 | 255 |
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:
Tas ir īslaicīgs: galu galā zilā pavediena mērķis ir iestatīt krāsu uz zilu.
- 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ītne | Paziņojums, apgalvojums | r | g | b | Krāsa |
neviena | objekts apzīmē zaļu | 0 | 255 | 0 | |
zils | zils pavediens izsauc setColor (0, 0, 255) | 0 | 255 | 0 | |
zils | checkRGBVals (0, 0, 255); | 0 | 255 | 0 | |
zils | šī.r = 0; | 0 | 255 | 0 | |
zils | tas.g = 0; | 0 | 255 | 0 | |
zils | zils tiek novērsts | 0 | 0 | 0 | |
sarkans | sarkanais pavediens izsauc getColor () | 0 | 0 | 0 | |
sarkans | int [] retVal = jauns int [3]; | 0 | 0 | 0 | |
sarkans | retVal [0] = 0; | 0 | 0 | 0 | |
sarkans | retVal [1] = 0; | 0 | 0 | 0 | |
sarkans | retVal [2] = 0; | 0 | 0 | 0 | |
sarkans | atgriezties retVal; | 0 | 0 | 0 | |
sarkans | sarkans pavediens atgriež melnu | 0 | 0 | 0 | |
zils | vēlāk zils pavediens turpinās | 0 | 0 | 0 | |
zils | tas.b = 255 | 0 | 0 | 0 | |
zils | zils pavediens atgriežas | 0 | 0 | 255 | |
neviena | objekts apzīmē zilu | 0 | 0 | 255 |
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:
- Sinhronizēt kritiskās sadaļas
- Padariet to nemainīgu
- 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.