Sincronizarea firelor

Sincronizarea firelor

Modul de operare cu mai multe fire deschide noi oportunități pentru programatori, dar pentru aceste oportunități, trebuie să plătiți pentru complexitatea procesului de proiectare a aplicației și de depanare.





Principala dificultate cu care se confruntă programatorii care nu au creat anterior aplicații multi-threaded este sincronizarea firelor concurente.

Pentru ce și când este necesar?

Un program cu un singur filet, cum ar fi programul MS-DOS, obține toate resursele calculatorului la dispoziția sa. Deoarece există un singur proces într-un sistem cu un singur filet, acesta utilizează aceste resurse în ordinea care corespunde logicii programului. Procesele și firele care rulează simultan într-un sistem multithreaded pot încerca să acceseze aceleași resurse în același timp, ceea ce poate duce la performanțe incorecte ale aplicațiilor.







Să explicăm acest lucru printr-un exemplu simplu.

Să creăm un program care să efectueze operațiuni cu un cont bancar. Operațiunea de retragere a unei anumite sume de bani dintr-un cont poate apărea în următoarea ordine:
  • primul pas verifică suma totală de bani stocată în cont;
  • dacă suma totală este egală sau depășește suma sumei retrase, suma totală este redusă cu suma necesară;
  • valoarea soldului este înscrisă în contul curent.

Dacă operația de reducere a contului curent este efectuată într-un sistem cu un singur filet, atunci nu apar probleme. Cu toate acestea, să presupunem că două procese încearcă să efectueze operația descrisă cu același cont în același timp. Să fie în cont este de 5 milioane de dolari, iar ambele procese încearcă să elimine de la el 3 milioane de dolari.

Să spunem că evenimentele se desfășoară după cum urmează:
  • Primul proces verifică starea contului curent și verifică că acesta deține 5 milioane dolari;
  • Al doilea proces verifică starea contului curent și asigură, de asemenea, că acesta deține 5 milioane dolari;
  • primul proces reduce contul cu 3 milioane de dolari și înregistrează soldul (2 milioane dolari) din contul curent;
  • cel de-al doilea proces efectuează aceeași operațiune, deoarece, după verificare, consideră că contul deține încă 5 milioane de dolari.

Ca rezultat, sa constatat că 6 milioane de dolari au fost retrași din contul în care erau 5 milioane de dolari, iar în continuare au rămas 2 milioane de dolari! Total - banca a suferit daune de 3 milioane de dolari.

Cum de a face un program de reducere a contului, așa că nu a permis să se ridice așa?

Este foarte simplu - pentru durata operațiunilor pe cont printr-un singur proces, este necesar să interzic accesul la acest cont din alte procese. În acest caz, scenariul programului ar trebui să fie după cum urmează:
  • procesul blochează contul pentru a efectua operațiuni prin alte procese, obținându-l în proprietate exclusivă;
  • procesul realizează procedura de reducere a contului și scrie contului curent o nouă valoare a soldului;
  • Procesul deblochează contul, permițând altor procese să efectueze operațiuni.

Când primul proces blochează un cont, devine indisponibil pentru alte procese. Dacă al doilea proces încearcă, de asemenea, să blocheze același cont, acesta va fi pus într-o stare de așteptare. Când primul proces reduce contul și rămâne 2 milioane de dolari, cel de-al doilea proces va fi deblocat. El va verifica soldul, asigurați-vă că suma este insuficientă și nu va efectua operația.

Astfel, într-un mediu multithreaded, este necesar să se sincronizeze firele atunci când se accesează resurse critice. Dacă operațiile cu secvențe greșite sunt efectuate pe astfel de resurse, acest lucru va duce la erori greu de găsit.

În limba de programare Java, există mai multe instrumente pentru sincronizarea firelor pe care le vom analiza acum.

Sincronizarea metodelor

Posibilitatea sincronizării este integrată în fiecare obiect creat de aplicația Java. Pentru aceasta, obiectele sunt prevăzute cu niște blocuri, care pot fi utilizate pentru a bloca fluxurile care accesează aceste obiecte.

Pentru a utiliza clapete, puteți declara metoda corespunzătoare ca fiind sincronizată. făcându-l sincronizat:

Atunci când se numește o metodă sincronizată, obiectul corespunzător (în care este definit) este blocat pentru a fi utilizat de alte metode sincronizate. Ca urmare, este împiedicată înregistrarea simultană prin două metode de valori în zona de memorie care aparține acestui obiect.

Utilizarea metodelor sincronizate este o modalitate simplă de a sincroniza firele care accesează resurse critice comune, cum ar fi contul bancar descris mai sus.

Rețineți că nu este necesară sincronizarea întregii metode - puteți sincroniza doar o piesă critică de cod.

Aici, sincronizarea este efectuată pentru obiectul Cont.

Blocarea debitului

Un fir sincronizat, definit ca o metodă sincronizată, poate intra automat în stare blocată atunci când încearcă să acceseze o resursă ocupată de o altă metodă sincronizată sau când efectuează anumite operații de intrare sau ieșire. Cu toate acestea, în unele cazuri, este util să aveți mai multe instrumente de sincronizare cu granulație fină care să permită utilizarea explicită a aplicației la cerere.

Blocarea pentru o anumită perioadă de timp

Utilizând metoda somnului, puteți bloca fluxul pentru o anumită perioadă de timp:

În acest exemplu, firul filetat este suspendat timp de 500 de milisecunde. Rețineți că în timp ce așteptați, firul suspendat nu are resurse CPU.

Deoarece metoda de somn poate crea o InterruptedException, este necesar să se furnizeze procesarea acesteia. Pentru aceasta, am folosit clauzele de încercare și de captură.

Suspendarea temporară și reluarea muncii

Metodele de suspendare și de reluare permit, respectiv, suspendarea temporară și reluarea activității fluxului.

În următorul fragment de cod, firul m_Rectangles se oprește atunci când cursorul mouse-ului este peste fereastra de applet:

Activitatea thread-ului reia când cursorul mouse-ului părăsește fereastra de applet:

Așteptarea notificării

Dacă aveți nevoie să organizați interacțiunea firelor astfel încât un fir să controleze funcționarea altor fire sau a altor fire, puteți utiliza metodele de așteptare. notificați și notificațiAll. definite în Obiectul de clasă.

Metoda de așteptare poate fi utilizată fie cu un parametru, fie fără un parametru. Această metodă convertește firul într-o stare de așteptare în care acesta va rămâne până când notificarea notifică, va notifica metoda AllAll pentru firul sau până când expiră perioada de timp specificată în parametrul de așteptare.

Cum se utilizează metoda de așteptare, notificare și notificareAll methods?

Metoda care va fi introdusă în starea de așteptare trebuie să fie sincronizată, adică ar trebui să fie descrisă ca fiind sincronizată:

În acest exemplu, în cadrul metodei de rulare, este definită o buclă care apelează metoda așteptare fără parametri. De fiecare dată când trece buclele următoare, metoda de rulare este pusă într-o stare de așteptare până când un alt fir execută notificarea utilizând metoda de notificare.

Mai jos am dat un exemplu de fir care sună la metoda de notificare:

Rețineți că, deși metoda de rulare în sine nu este sincronizată, apelul pentru notificare este executat în modul sincronizat. Obiectul de sincronizare este firul pentru care este apelată metoda de notificare.

Așteptând ca firul să se termine

Folosind metoda join, puteți aștepta ca firul să se termine. pentru care se numește această metodă.

Există trei definiții ale metodei de conectare:

Primul dintre ele efectuează așteptarea fără limită de timp, pentru al doilea, așteptarea va fi întreruptă forțat după millis milisecunde și pentru a treia milisecundă și nanosecunde nanoscale. Rețineți că, în realitate, nu veți putea specifica timpul până la precizia nanosecundelor, deoarece discrepanța cronometrului de sistem a calculatorului este mult mai mare.







Articole similare

Trimiteți-le prietenilor: