CodeGym /Java Blog /Random-IT /Sincronizzazione dei thread. L'operatore sincronizzato
John Squirrels
Livello 41
San Francisco

Sincronizzazione dei thread. L'operatore sincronizzato

Pubblicato nel gruppo Random-IT
CIAO! Oggi continueremo a considerare le caratteristiche della programmazione multithread e parleremo della sincronizzazione dei thread. Sincronizzazione dei thread.  L'operatore sincronizzato - 1

Cos'è la sincronizzazione in Java?

Al di fuori del dominio di programmazione, implica una disposizione che consente a due dispositivi o programmi di lavorare insieme. Ad esempio, uno smartphone e un computer possono essere sincronizzati con un account Google e un account di un sito Web può essere sincronizzato con account di social network in modo da poterli utilizzare per accedere. La sincronizzazione dei thread ha un significato simile: è una disposizione in cui i thread interagiscono con l'un l'altro. Nelle lezioni precedenti, i nostri thread vivevano e lavoravano separatamente l'uno dall'altro. Uno ha eseguito un calcolo, un secondo ha dormito e un terzo ha visualizzato qualcosa sulla console, ma non hanno interagito. Nei programmi reali, tali situazioni sono rare. Più thread possono lavorare attivamente e modificare lo stesso set di dati. Questo crea problemi. Immagina più thread che scrivono testo nello stesso posto, ad esempio, in un file di testo o nella console. In questo caso, il file o la console diventa una risorsa condivisa. I thread non sono a conoscenza dell'esistenza l'uno dell'altro, quindi scrivono semplicemente tutto ciò che possono nel tempo loro assegnato dallo scheduler dei thread. In una lezione recente, abbiamo visto un esempio di dove questo porta. Ricordiamolo ora: Sincronizzazione dei thread.  L'operatore sincronizzato - 2Il motivo sta nel fatto che i thread stanno lavorando con una risorsa condivisa (la console) senza coordinare le loro azioni tra loro. Se lo scheduler del thread alloca il tempo al Thread-1, scrive istantaneamente tutto sulla console. Ciò che altri thread sono o non sono già riusciti a scrivere non ha importanza. Il risultato, come puoi vedere, è deprimente. Ecco perché hanno introdotto un concetto speciale, il mutex (esclusione reciproca) , nella programmazione multithread. Lo scopo di un mutexè fornire un meccanismo in modo che solo un thread abbia accesso a un oggetto in un determinato momento. Se Thread-1 acquisisce il mutex dell'oggetto A, gli altri thread non saranno in grado di accedere e modificare l'oggetto. Gli altri thread devono attendere fino al rilascio del mutex dell'oggetto A. Ecco un esempio dalla vita: immagina che tu e altri 10 estranei partecipiate a un esercizio. A turno, devi esprimere le tue idee e discutere qualcosa. Ma poiché vi vedete per la prima volta, per non interrompervi costantemente e andare su tutte le furie, usate una "palla parlante": solo la persona con la palla può parlare. In questo modo si finisce per avere una buona e fruttuosa discussione. Essenzialmente, la palla è un mutex. Se il mutex di un oggetto è nelle mani di un thread, gli altri thread non possono lavorare con l'oggetto.Objectclass, il che significa che ogni oggetto in Java ne ha uno.

Come funziona l'operatore sincronizzato

Conosciamo una nuova parola chiave: sincronizzato . Viene utilizzato per contrassegnare un determinato blocco di codice. Se un blocco di codice è contrassegnato con la synchronizedparola chiave, quel blocco può essere eseguito solo da un thread alla volta. La sincronizzazione può essere implementata in diversi modi. Ad esempio, dichiarando un intero metodo da sincronizzare:

public synchronized void doSomething() {

   // ...Method logic
}
Oppure scrivi un blocco di codice in cui la sincronizzazione viene eseguita utilizzando un oggetto:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...Some logic available simultaneously to all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
Il significato è semplice. Se un thread entra nel blocco di codice contrassegnato con la synchronizedparola chiave, cattura istantaneamente il mutex dell'oggetto e tutti gli altri thread che tentano di entrare nello stesso blocco o metodo sono costretti ad attendere fino a quando il thread precedente completa il suo lavoro e rilascia il monitor. Sincronizzazione dei thread.  L'operatore sincronizzato - 3A proposito! Durante il corso, hai già visto esempi di synchronized, ma sembravano diversi:

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
L'argomento è nuovo per te. E, naturalmente, ci sarà confusione con la sintassi. Quindi, memorizzalo subito per evitare di essere confuso in seguito dai diversi modi di scriverlo. Questi due modi di scriverlo significano la stessa cosa:

public void swap() {

   synchronized (this)
   {
       // ...Method logic
   }
}


public synchronized void swap() {

   }
}
Nel primo caso, crei un blocco di codice sincronizzato immediatamente dopo aver inserito il metodo. È sincronizzato dall'oggetto this, cioè l'oggetto corrente. E nel secondo esempio, applichi la synchronizedparola chiave all'intero metodo. Ciò rende superfluo indicare in modo esplicito l'oggetto utilizzato per la sincronizzazione. Poiché l'intero metodo è contrassegnato con la parola chiave, il metodo verrà automaticamente sincronizzato per tutte le istanze della classe. Non ci tufferemo in una discussione su quale sia il modo migliore. Per ora, scegli il modo che preferisci :) La cosa principale è ricordare: puoi dichiarare un metodo sincronizzato solo quando tutta la sua logica viene eseguita da un thread alla volta. Ad esempio, sarebbe un errore sincronizzare il seguente doSomething()metodo:

public class Main {

   private Object obj = new Object();

   public void doSomething() {

       // ...Some logic available simultaneously to all threads

       synchronized (obj) {

           // Logic available to just one thread at a time
       }
   }
}
Come puoi vedere, parte del metodo contiene una logica che non richiede la sincronizzazione. Quel codice può essere eseguito da più thread contemporaneamente e tutti i punti critici sono separati in un synchronizedblocco separato. E un'altra cosa. Esaminiamo da vicino il nostro esempio della lezione con lo scambio di nomi:

public void swap()
{
   synchronized (this)
   {
       // ...Method logic
   }
}
Nota: la sincronizzazione viene eseguita utilizzandothis. Cioè, usando unMyClassoggetto specifico. Supponiamo di avere 2 thread (Thread-1eThread-2) e un soloMyClass myClassoggetto. In questo caso, seThread-1si chiama ilmyClass.swap()metodo, il mutex dell'oggetto sarà occupato e quando si tenta di chiamare ilmyClass.swap()metodoThread-2si bloccherà in attesa del rilascio del mutex. Se avremo 2 thread e 2MyClassoggetti (myClass1emyClass2), i nostri thread possono facilmente eseguire simultaneamente i metodi sincronizzati su oggetti diversi. Il primo thread esegue questo:

myClass1.swap();
Il secondo esegue questo:

myClass2.swap();
In questo caso, la synchronizedparola chiave all'interno del swap()metodo non influenzerà il funzionamento del programma, poiché la sincronizzazione viene eseguita utilizzando un oggetto specifico. E in quest'ultimo caso, abbiamo 2 oggetti. Pertanto, i thread non creano problemi l'uno per l'altro. Dopotutto, due oggetti hanno 2 mutex diversi e l'acquisizione di uno è indipendente dall'acquisizione dell'altro .

Particolarità della sincronizzazione nei metodi statici

Ma cosa succede se è necessario sincronizzare un metodo statico ?

class MyClass {
   private static String name1 = "Ally";
   private static String name2 = "Lena";

   public static synchronized void swap() {
       String s = name1;
       name1 = name2;
       name2 = s;
   }

}
Non è chiaro quale ruolo giocherà qui il mutex. Dopotutto, abbiamo già stabilito che ogni oggetto ha un mutex. Ma il problema è che non abbiamo bisogno di oggetti per chiamare il MyClass.swap()metodo: il metodo è statico! Allora, qual è il prossimo? :/ In realtà non ci sono problemi qui. I creatori di Java si sono occupati di tutto :) Se un metodo che contiene logica concorrente critica è statico, la sincronizzazione viene eseguita a livello di classe. Per maggiore chiarezza, possiamo riscrivere il codice precedente come segue:

class MyClass {
   private static String name1 = "Ally";
   private static String name2 = "Lena";

   public static void swap() {

       synchronized (MyClass.class) {
           String s = name1;
           name1 = name2;
           name2 = s;
       }
   }

}
In linea di principio, avresti potuto pensarci tu stesso: poiché non ci sono oggetti, il meccanismo di sincronizzazione deve in qualche modo essere integrato nella classe stessa. Ed è così: possiamo usare le classi per sincronizzare.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION