Synccronizzazione in Java

Che cos'è la Synccronizzazione in Java?

In Java, la sincronizzazione si riferisce alla capacità di controllare l'accesso di più thread a qualsiasi risorsa condivisa. È un'opzione ideale quando vogliamo consentire a un solo thread di accedere alla risorsa condivisa.

Questo approccio di esecuzione è solitamente definito programmazione `asincrona`. Ci sono anche thread su questi processori, che sono processi leggeri che possono eseguire istruzioni simultaneamente.

Tipi di Synccronizzazione

Esistono due tipi di metodi di sincronizzazione in Java:

1) Sincronizzazione dei processi

2) Sincronizzazione dei thread.

Studiamo Thread e Sincronizzazione dei processi in dettaglio.

Sincronizzazione dei processi: Gestisce la sincronizzazione tra programmi. Ad esempio, programmi come `Microsoft Word" e "Acrobat reader" vengono eseguiti come processi singoli.

Sincronizzazione dei thread: L'esecuzione simultanea della risorsa critica da parte di due o più Thread è denominata Thread Syncronizzazione. È possibile raggrupparsi ulteriormente per comunicazioni mutuamente esclusive e inter-thread.

Cos'è Lock-in Java?

Bloccare Java è costruito attorno a un'entità interna nota come monitor o serratura. Tutti gli oggetti hanno un lucchetto ad essi associato. Pertanto, il thread che necessita di un accesso coerente ai campi di un oggetto deve acquisire il blocco dell'oggetto prima di accedervi e rilasciare il blocco una volta terminato il lavoro. Ciò garantisce che solo un thread alla volta acceda ai dati condivisi.

Programma multithread con sincronizzazione

Un programma multithread è un metodo o un blocco protetto dalle interferenze di altri thread che condividono la stessa risorsa indicata tramite la parola chiave `synchronized`.

Utilizzando il metodo sincronizzato

Ogni metodo dichiarato come sincronizzato è noto come Metodo sincronizzato. Viene anche utilizzato per bloccare un oggetto per qualsiasi risorsa condivisa. Quindi, quando un thread invoca un metodo sincronizzato, prende automaticamente possesso del blocco per quell'oggetto e lo rilascia quando ha terminato il suo compito.

Nota: La parola chiave synchronized non può funzionare con classi e variabili. Solo metodi e blocchi possono essere usati con la parola chiave.

Perché usare il SyncMetodo cronizzato?

  • Viene utilizzato per bloccare un oggetto per qualsiasi risorsa condivisa.
  • L'oggetto ottiene il blocco ogni volta che viene chiamato il metodo sincronizzato.
  • Il blocco non viene rilasciato finché il thread non completa la sua funzione

Sintassi:

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();
    }
}

Spiegazione del codice:

Esegui questo esempio e osserva che il thread "0" ottiene prima il blocco dell'oggetto "mathService" e utilizza esclusivamente questo blocco fino al completamento dell'esecuzione. I thread "0" e "1" non sono interlacciati in questo codice. L'output è quello mostrato di seguito.

Produzione:

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

Utilizzo di un blocco sincronizzato

Supponiamo che non si voglia sincronizzare l'intero metodo. Invece, si vogliono sincronizzare alcune righe di codice. A quel punto, il Syncil blocco cronizzato ha aiutato a sincronizzare quello selezionato Java codice. SyncI blocchi di metodo sincronizzati vengono eseguiti sul metodo, mentre i blocchi di blocco sincronizzati vengono eseguiti sull'oggetto.

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();
    }
}

Spiegazione del codice:

Eseguendo questo codice, noterai che funziona senza alcuna interferenza. Nel metodo sincronizzato, il blocco è applicato dal metodo, ma nel blocco sincronizzato, il blocco è applicato dall'oggetto. Assicurati che l'output sia come mostrato di seguito.

Produzione:

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

Spiegazione del codice:

Quando esegui questo codice, noterai che funziona senza interferenze, che è ciò che ci aspettavamo. Nel metodo sincronizzato, il blocco è applicato dal metodo, ma nel metodo del blocco sincronizzato, il blocco è applicato dall'oggetto.

Utilizzo della sincronizzazione statica

In Java sincronizzazione, se c'è più di un oggetto, due thread possono acquisire i blocchi ed entrare in un blocco o in un blocco sincronizzato, con un blocco separato per ogni oggetto. Per evitare ciò, è possibile utilizzare la sincronizzazione statica. Syncle parole chiave sincronizzate verranno utilizzate prima dei metodi statici.

Nota: Nella sincronizzazione statica, l'accesso al blocco avviene sulla classe, non sull'oggetto e sul metodo.

Codice per dimostrare il problema del blocco di più oggetti

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();
    }
}

Spiegazione del codice:

Quando creiamo un'altra istanza di `MathService`, introduciamo un'interferenza nei thread poiché verranno interlacciati con i due oggetti. Nota che il thread "0" e il thread "2" sono interlacciati con i due oggetti, mentre i thread "1" e "3" sono interlacciati con i due oggetti.

Produzione:

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

Stesso codice utilizzando il metodo statico sincronizzato

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();
    }
}

Esegui il codice sopra e nota che ora abbiamo eliminato l'interferenza del thread. L'output del codice è mostrato di seguito.

Produzione:

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

Vantaggi dell'utilizzo della sincronizzazione

Ecco i vantaggi di lavorare con applicazioni simultanee:

  • L'obiettivo principale della sincronizzazione in Java è quello di impedire che i dati siano incoerenti impedendo l'interferenza dei thread.
  • La parola chiave sincronizzata in Java fornisce il blocco, che garantisce un accesso mutuamente esclusivo alla risorsa condivisa e impedisce la competizione tra dati.
  • Impedisce inoltre il riordino delle istruzioni di codice da parte di compilatore, che può causare un sottile problema di concorrenza se non utilizziamo parole chiave volatili o sincronizzate.
  • SyncLa parola chiave hronized legge i dati dalla memoria principale, dalla cache e quando rilascia il blocco.
  • Inoltre, elimina le operazioni di scrittura dalla memoria principale, eliminando gli errori di incoerenza della memoria.

Svantaggi di SyncMeccanismo di cronizzazione

SyncI meccanismi di armonizzazione hanno scarse prestazioni.

Per esempio

  • Supponiamo che ci siano cinque processi, A1, A2, A3, A4 e A5.
  • Stanno aspettando che le risorse condivise accedano a un thread alla volta.
  • Tutti i processi vengono tenuti in attesa, quindi l'ultimo in coda deve attendere fino al completamento di tutti gli altri processi.

Sommario

  • SyncL'ronizzazione si riferisce alla capacità di controllare l'accesso di più thread a qualsiasi risorsa condivisa.
  • Java ha due tipi di metodi di sincronizzazione: 1) Sincronizzazione dei processi e 2) Sincronizzazione dei thread.
  • Bloccare Java è costruito attorno a un'entità interna nota come monitor o serratura.
  • Un programma multithread è un metodo o un blocco protetto dalle interferenze di altri thread che condividono la stessa risorsa indicata mediante la parola chiave `synchronized`.
  • Qualsiasi metodo dichiarato come sincronizzato è noto come metodo sincronizzato.
  • In Java, i blocchi di metodo sincronizzati vengono eseguiti sul metodo, mentre i blocchi di blocco sincronizzati vengono eseguiti sull'oggetto.
  • Nella sincronizzazione statica, l'accesso al blocco avviene sulla classe, non sull'oggetto e sul metodo.
  • L'obiettivo principale della sincronizzazione in Java è quello di impedire che i dati siano incoerenti impedendo l'interferenza dei thread.
  • Il più grande svantaggio di questo metodo è che tutti i processi vengono tenuti in attesa, quindi l'ultimo in coda deve attendere fino al completamento di tutti gli altri processi.