Synchronizare în Java
Ce Este Synchronizare în Java?
In Java, sincronizarea se referă la capacitatea de a controla accesul mai multor fire de execuție la orice resursă partajată. Este o opțiune ideală în care dorim să permitem unui singur fir să acceseze resursa partajată.
Această abordare a execuției este de obicei denumită programare „asincronă”. Există, de asemenea, fire de execuție pe aceste procesoare, care sunt procese ușoare care pot executa instrucțiuni simultan.
Tipuri de Synchronizare
Există două tipuri de metode de sincronizare în Java:
1) Sincronizarea proceselor
2) Sincronizarea firelor.
Să studiem Thread și Sincronizarea proceselor detaliat.
Sincronizarea procesului: Gestionează sincronizarea între programe. De exemplu, programe precum `Microsoft Word` și `Acrobat reader` rulează ca procese individuale.
Sincronizarea firelor: Execuția concomitentă a resursei critice de către două sau mai multe Thread-uri se numește Thread Synchronizare. Puteți fi grupați în continuare la comunicarea care se exclude reciproc și între fire.
Ce este Lock in Java?
Încuie-te Java este construit în jurul unei entități interne cunoscute sub numele de monitor sau lacăt. Toate obiectele au asociată o încuietoare. Deci, firul de execuție care are nevoie de acces consecvent la câmpurile unui obiect trebuie să obțină blocarea obiectului înainte de a le accesa și eliberează blocarea când lucrarea este terminată. Acest lucru asigură că doar un fir accesează datele partajate la un moment dat.
Program multithread cu sincronizare
Un program cu mai multe fire este o metodă sau un bloc protejat de interferența de la alte fire care partajează aceeași resursă indicată folosind cuvântul cheie `sincronizat`.
Folosind metoda sincronizată
Orice metodă care este declarată ca sincronizată este cunoscută ca o metodă sincronizată. De asemenea, este folosit pentru a bloca un obiect pentru orice resursă partajată. Deci, atunci când un fir invocă o metodă sincronizată. Acesta preia automat încuietoarea pentru acel obiect și îl eliberează când și-a terminat sarcina.
Notă: Cuvântul cheie sincronizat nu poate funcționa cu clase și variabile. Cu cuvântul cheie pot fi folosite doar metode și blocuri.
De ce să folosiți SyncMetoda hronizată?
- Este folosit pentru blocarea unui obiect pentru orice resurse partajate.
- Obiectul primește blocarea ori de câte ori este apelată metoda sincronizată.
- Blocarea nu se eliberează până când firul își încheie funcția
Sintaxă:
Acess_modifiers synchronized return_type method_name (Method_Parameters) { }
class MathService { synchronized void getSumOfArray(int[] numbers) { int sum = 0; for (int number : numbers) { System.out.println(Thread.currentThread() .getName() + " adds " + sum + " to " + number + " to get -> " + (sum += number)); try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } public class Synchronization { public static void main(String[] args) { MathService mathService = new MathService(); Thread threadOne = new Thread(() -> mathService.getSumOfArray(new int[]{10, 11, 12})); Thread threadTwo = new Thread(() -> mathService.getSumOfArray(new int[]{20, 21, 22})); threadOne.start(); threadTwo.start(); } }
Explicația codului:
Rulați acest exemplu și observați că firul `0` primește mai întâi blocarea obiectului `mathService` și folosește această blocare exclusiv până la finalizarea executării. Thread `0` și `1` nu sunt intercalate în acest cod. Ieșirea este așa cum se arată mai jos.
ieșire:
Thread-0 adds 0 to 10 to get -> 10 Thread-0 adds 10 to 11 to get -> 21 Thread-0 adds 21 to 12 to get -> 33 Thread-1 adds 0 to 20 to get -> 20 Thread-1 adds 20 to 21 to get -> 41 Thread-1 adds 41 to 22 to get -> 63
Folosind un bloc sincronizat
Să presupunem că nu doriți să sincronizați întreaga metodă. În schimb, doriți să sincronizați câteva linii de cod. La acea vreme, Syncblocul hronizat a ajutat la sincronizarea celui selectat Java cod. Syncblocările metodei cronizate sunt accesate pe metodă, în timp ce blocurile sincronizate sunt accesate pe obiect.
class MathService { void getSumOfArray(int[] numbers) { synchronized (this){ int sum = 0; for (int number : numbers) { System.out.println(Thread.currentThread() .getName() + " adds " + sum + " to " + number + " to get -> " + (sum += number)); try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } } public class Synchronization { public static void main(String[] args) { MathService mathService = new MathService(); Thread threadOne = new Thread(() -> mathService.getSumOfArray(new int[]{10, 11, 12})); Thread threadTwo = new Thread(() -> mathService.getSumOfArray(new int[]{20, 21, 22})); threadOne.start(); threadTwo.start(); } }
Explicația codului:
La rularea acestui cod, veți observa că funcționează fără interferențe. În metoda sincronizată, blocarea este aplicată de metodă, dar în blocul sincronizat, blocarea este aplicată de obiect. Asigurați-vă că rezultatul este așa cum se arată mai jos.
ieșire:
Thread-0 adds 0 to 10 to get -> 10 Thread-0 adds 10 to 11 to get -> 21 Thread-0 adds 21 to 12 to get -> 33 Thread-1 adds 0 to 20 to get -> 20 Thread-1 adds 20 to 21 to get -> 41 Thread-1 adds 41 to 22 to get -> 63
Explicația codului:
Când rulați acest cod, veți observa că funcționează fără interferențe, ceea ce ne așteptam. În metoda sincronizată, blocarea este aplicată prin metodă, dar în metoda blocului sincronizat, blocarea este aplicată de obiect.
Folosind sincronizarea statică
In Java sincronizare, dacă există mai mult de un obiect, două fire pot dobândi blocările și pot intra într-un bloc sau bloc sincronizat, cu o blocare separată pentru fiecare obiect. Pentru a evita acest lucru, poate fi utilizată sincronizarea statică. Synccuvintele cheie hronizate vor fi folosite înaintea metodelor statice.
Notă: În sincronizarea statică, accesul de blocare este pe clasă, nu pe obiect și metodă.
Cod pentru a demonstra problema blocării mai multor obiecte
class MathService { synchronized void getSumOfArray(int[] numbers) { int sum = 0; for (int number : numbers) { System.out.println(Thread.currentThread() .getName() + " adds " + sum + " to " + number + " to get -> " + (sum += number)); try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } public class Synchronization { public static void main(String[] args) { MathService mathService = new MathService(); MathService mathService1 = new MathService(); Thread threadOne = new Thread(() -> mathService.getSumOfArray(new int[]{10, 11, 12})); Thread threadTwo = new Thread(() -> mathService.getSumOfArray(new int[]{20, 21, 22})); Thread threadThree = new Thread(() -> mathService1.getSumOfArray(new int[]{10, 11, 12})); Thread threadFour = new Thread(() -> mathService1.getSumOfArray(new int[]{20, 21, 22})); threadOne.start(); threadTwo.start(); threadThree.start(); threadFour.start(); } }
Explicația codului:
Când creăm o altă instanță a `MathService`, introducem interferență în fire, deoarece acestea vor fi intercalate cu cele două obiecte. Rețineți că firul `0` și firul `2` sunt intercalate cu cele două obiecte, în timp ce firele `1` și `3` sunt intercalate cu cele două obiecte.
ieșire:
Thread-0 adds 0 to 10 to get -> 10 Thread-2 adds 0 to 10 to get -> 10 Thread-0 adds 10 to 11 to get -> 21 Thread-2 adds 10 to 11 to get -> 21 Thread-0 adds 21 to 12 to get -> 33 Thread-2 adds 21 to 12 to get -> 33 Thread-1 adds 0 to 20 to get -> 20 Thread-3 adds 0 to 20 to get -> 20 Thread-1 adds 20 to 21 to get -> 41 Thread-3 adds 20 to 21 to get -> 41 Thread-1 adds 41 to 22 to get -> 63 Thread-3 adds 41 to 22 to get -> 63
Același cod folosind metoda statică sincronizată
class MathService { synchronized static void getSumOfArray(int[] numbers) { int sum = 0; for (int number : numbers) { System.out.println(Thread.currentThread() .getName() + " adds " + sum + " to " + number + " to get -> " + (sum += number)); try { Thread.sleep(500); } catch (InterruptedException e) { throw new RuntimeException(e); } } } } public class Synchronization { public static void main(String[] args) { MathService mathService = new MathService(); MathService mathService1 = new MathService(); Thread threadOne = new Thread(() -> mathService.getSumOfArray(new int[]{10, 11, 12})); Thread threadTwo = new Thread(() -> mathService.getSumOfArray(new int[]{20, 21, 22})); Thread threadThree = new Thread(() -> mathService1.getSumOfArray(new int[]{10, 11, 12})); Thread threadFour = new Thread(() -> mathService1.getSumOfArray(new int[]{20, 21, 22})); threadOne.start(); threadTwo.start(); threadThree.start(); threadFour.start(); } }
Rulați codul de mai sus și rețineți că acum am eliminat interferența firelor. Ieșirea codului este prezentată mai jos.
ieșire:
Thread-0 adds 0 to 10 to get -> 10 Thread-0 adds 10 to 11 to get -> 21 Thread-0 adds 21 to 12 to get -> 33 Thread-3 adds 0 to 20 to get -> 20 Thread-3 adds 20 to 21 to get -> 41 Thread-3 adds 41 to 22 to get -> 63 Thread-2 adds 0 to 10 to get -> 10 Thread-2 adds 10 to 11 to get -> 21 Thread-2 adds 21 to 12 to get -> 33 Thread-1 adds 0 to 20 to get -> 20 Thread-1 adds 20 to 21 to get -> 41 Thread-1 adds 41 to 22 to get -> 63
Avantajele utilizării sincronizării
Iată avantajele când lucrați cu aplicații concurente:
- Obiectivul principal al sincronizării în Java este de a preveni datele inconsecvente prin prevenirea interferenței firelor.
- Cuvântul cheie sincronizat în Java asigură blocarea, care asigură accesul reciproc exclusiv la resursa partajată și previne cursa datelor.
- De asemenea, previne reordonarea instrucțiunilor de cod de către compilator, care poate cauza o problemă concomitentă subtilă dacă nu folosim cuvinte cheie volatile sau sincronizate.
- Synccuvântul cheie hronized citește datele din memoria principală decât cache și când eliberează blocarea.
- De asemenea, șterge operațiunile de scriere din memoria principală, eliminând erorile de inconsecvență a memoriei.
Dezavantaje ale SyncMecanismul de cronizare
SyncMecanismele de cronizare au performanțe slabe.
De exemplu
- Să presupunem că există cinci procese, A1, A2, A3, A4 și A5.
- Ei așteaptă ca resursele partajate să acceseze câte un fir la un moment dat.
- Toate procesele sunt menținute în așteptare, astfel încât ultimul din coadă trebuie să aștepte până când toate celelalte procese sunt finalizate.
Rezumat
- Synchronizarea se referă la capacitatea de a controla accesul mai multor fire la orice resursă partajată.
- Java are două tipuri de metode de sincronizare: 1) Sincronizarea proceselor și 2) Sincronizarea firelor.
- Încuie-te Java este construit în jurul unei entități interne cunoscute sub numele de monitor sau lacăt.
- Un program cu mai multe fire este o metodă sau un bloc protejat de interferența altor fire care partajează aceeași resursă indicată folosind cuvântul cheie `sincronizat`.
- Orice metodă care este declarată ca sincronizată este cunoscută ca metodă sincronizată.
- In Java, blocările de metodă sincronizată sunt accesate pe metodă, în timp ce blocările de bloc sincronizate sunt accesate pe obiect.
- În sincronizarea statică, accesul de blocare este pe clasă, nu pe obiect și metodă.
- Obiectivul principal al sincronizării în Java este de a preveni datele inconsecvente prin prevenirea interferenței firelor.
- Cel mai mare dezavantaj al acestei metode este că toate procesele sunt ținute în așteptare, astfel încât ultimul din coadă trebuie să aștepte până când toate celelalte procese sunt finalizate.