Multithreading și sincronizare

Probleme legate de probleme

Problema unu - accesul la o singură resursă din mai multe fire. Am descris deja problema cu o lopată. Puteți extinde opțiunea - există un rezervor de apă (cu o singură robinet), minerii însetat 25 și 5 cani deloc.





Va trebui să negociem, altfel uciderea poate începe. Și nu numai că trebuie să păstrăm integritatea cercurilor - trebuie să organizăm totul pentru a bea toată lumea. Acest lucru merge parțial la problema numărul doi.






A doua problemă este sincronizarea interacțiunii. Cumva mi sa oferit sarcina de a scrie un program simplu pentru două fluxuri pentru a juca ping-pong. Unul scrie "Ping", iar al doilea - "Pong". Dar trebuie să facă acest lucru la rândul său. Acum, să ne imaginăm că trebuie să facem aceeași sarcină, dar pentru 4 fire - jucăm o pereche pentru un cuplu.

Ie formularea problemelor este foarte simplă. Timp - este necesar să se organizeze accesul comandat și sigur la resursa partajată. Două - trebuie să executați fire într-o ordine.
Problema se află în spatele implementării. Și aici ne confruntăm cu o mulțime de dificultăți, despre care vorbește cu un prehannel (și poate cu bună știință). Să începem cu resursa partajată.

O resursă partajată pentru mai multe fire

Vă sugerez imediat demonstrarea problemei pe un exemplu simplu. Sarcina sa este de a rula 200 de fire din clasa CounterTread. Fiecare fir primește o referință la un singur obiect Counter. În timpul execuției, firul provoacă această metodă să mărească metoda de verificare de o mie de ori. Metoda mărește contorul variabil cu 1. Fugind 200 de fire, așteptăm ca acestea să se termine (adormiți doar 1 secundă - acest lucru este suficient). Și la final vom imprima rezultatul. Uitați-vă la cod - în opinia mea, totul este destul de transparent acolo:

Accesul la resursele partajate este una dintre cele mai mari probleme ale multithreading. Pentru că este foarte insidioasă. Puteți face totul foarte fiabil, dar performanța va scădea. Și, de îndată ce dați "slăbiciune" (conștient, pentru productivitate), va apărea neapărat o situație în care "slăbiciunea" va ieși în toată gloria sa.

Cuvantul magic este sincronizat

Ce se poate face pentru a scăpa de situația în care am căzut cu fluxurile noastre remarcabile. Să începem cu puțină speculație. Când ajungem la magazin, atunci pentru plată ajungem la checkout. Casierul servește doar o singură persoană la un moment dat. Suntem cu toții în linie pentru ea. De fapt, casierul devine o resursă exclusivă pe care un singur cumpărător o poate folosi la un moment dat. În multithreading, este sugerată exact aceeași metodă - puteți defini o anumită resursă ca fiind furnizată exclusiv unui singur fir la un moment dat. O astfel de resursă se numește "monitor". Acesta este obiectul cel mai obișnuit pe care un fir trebuie să îl "capteze". Toate firele care doresc să acceseze acest monitor (obiect) sunt aliniate. Și pentru asta nu trebuie să scrieți un cod special - încercați doar să "apucați" monitorul. Dar cum să desemnați acest lucru. Să înțelegem.
Vă propun să rulați exemplul nostru, dar cu un cuvânt suplimentar în descrierea metodei increaseCounter este cuvântul sincronizat.

Utilizarea separată a sincronizării face în mod esențial același lucru - mai întâi blochează / blochează obiectul care îi este transmis în paranteze și apoi începe executarea codului care este în interiorul sincronizat. Aici este necesar să se ia în considerare faptul că blocarea nu va avea loc la intrarea la metodă, ci la intrarea în blocul sincronizat. Apropo de acest lucru, puteți sincroniza într-un singur obiect prin mai multe. De exemplu, două metode blochează un obiect, iar alte două blochează un alt obiect. Apoi puteți apela metode din diferite grupuri în același timp. Am folosit asta în practica mea.
În principiu, ideea de sincronizare este epuizată. Acum utilizarea sa corectă devine importantă. Deoarece, pe de o parte, poate fi dorința de a face toate metodele sincronizate. dar va afecta performanța - cred că acest lucru este evident. Pe de altă parte, pot exista dificultăți în manipularea inexactă a obiectelor nesincronizate. Deci, fii vigilent.

Clase de siguranță pentru filete - filet sigur

Metoda getCounter poate fi pur și simplu declarată în documentație drept thread-safe. Și dacă în clasa Counter pentru a implementa metoda setCounter, atunci realizarea acestor două metode sincronizate nu are sens.

Ei bine, aici a fost sarcina de a arăta că multe fire corupte datele. Și după acel spectacol cum poate fi rezolvată aceasta. Am sentimentul că nimeni nu citește textul explicativ, ci doar să privească codul și să tragă concluzii numai în privința acestuia. Da, codul este într-o oarecare măsură controversat, dar arată exact ideea pe care am încercat să o arăt.

Scopul declarației mele nu a fost critica, ci o discuție ulterioară a problemelor multithreading.

Acest lucru se întâmplă uneori - nu am înțeles motivele.

Apelarea Thread.sleep () oprește firul curent. Ie cele 200 care rulează, au lucrat cinstit. Și firul din interiorul metodei principale este în așteptare.

Dragă administrator, înțeleg corect că există un fir în interiorul metodei principale, în care (în fir) se pornește un cod (loop), care la rândul său pornește 200 de fire care funcționează; Procedând astfel, vom întrerupe firul metodei principale astfel încât să aștepte ca cele 200 de fire să fie procesate înainte ca metoda principală să-și termine operațiunea.
Sau este pornită firul metodei principale, în paralel cu lansarea ei 200; Thread.sleep (1000) le permite să lucreze, apoi se reia principalele metode de metode, aruncă mesajul în consola și apoi se închide.
Îmi pare rău pentru întrebare, în cursul inițial Java, îl "mestecați"?

Da, metoda principală este executată într-un fir separat. Da, el lansează alte fire și se adoarme să demonstreze că alte fire au terminat.
Fluxurile sunt în principiu "egale". Atât firul principal cât și toate firele pe care le-a lansat.
Da, în anul inițial, toți l-am mestecat și am făcut multe exemple.

O astfel de sarcină: există multifuncționale, se pot imprima sau se pot scana simultan. Există două imprimante și două scanere. Scrieți programul astfel încât, la un moment dat, echipamentul MFP să fie disponibil numai unei singure imprimante și unui scaner, dar nu două imprimante / scanere simultan. Ie la un moment dat, o pagină poate fi tipărită și scanată în același timp, dar nu puteți imprima / scana simultan două pagini. Funcționarea imprimantei / scanerului este afișată în consola: "print ..", "scan ..". Cumva, este scris scrupulos, care a înțeles - explicați mai detaliat.

Primul exemplu funcționează fără sincronizare dacă firul principal al firului va aștepta ca firele pe care le-a generat să se termine înaintea șirului de ieșire al valorii variabilei contra cu apelul join () pe firele generate în bucla principală. Destul de ciudat, funcționează nu numai cu tipul int, ci și cu contorul variabil lung.

Lasă un răspuns Anulează răspunsul







Articole similare

Trimiteți-le prietenilor: