Ho alcune domande sull'uso e sul significato della synchronized
parola chiave.
- Qual è il significato della
synchronized
parola chiave? - Quando dovrebbero essere i metodi
synchronized
? - Cosa significa programmaticamente e logicamente?
Ho alcune domande sull'uso e sul significato della synchronized
parola chiave.
synchronized
parola chiave?synchronized
?Risposte:
La synchronized
parola chiave riguarda thread diversi che leggono e scrivono nelle stesse variabili, oggetti e risorse. Questo non è un argomento banale in Java, ma ecco una citazione da Sun:
synchronized
i metodi consentono una semplice strategia per prevenire l'interferenza del thread e gli errori di coerenza della memoria: se un oggetto è visibile a più di un thread, tutte le letture o scritture nelle variabili di quell'oggetto vengono eseguite tramite metodi sincronizzati.
In poche parole: quando hai due thread che leggono e scrivono nella stessa 'risorsa', dì una variabile denominata foo
, devi assicurarti che questi thread accedano alla variabile in modo atomico. Senza la synchronized
parola chiave, il tuo thread 1 potrebbe non vedere il thread di modifica 2 fatto foo
, o peggio, potrebbe essere cambiato solo per metà. Questo non sarebbe quello che ti aspetti logicamente.
Ancora una volta, questo è un argomento non banale in Java. Per saperne di più, esplora gli argomenti qui su SO e gli Interwebs su:
Continua ad esplorare questi argomenti fino a quando il nome "Brian Goetz" non viene associato in modo permanente al termine "concorrenza" nel tuo cervello.
Beh, penso che ne abbiamo abbastanza di spiegazioni teoriche, quindi considera questo codice
public class SOP {
public static void print(String s) {
System.out.println(s+"\n");
}
}
public class TestThread extends Thread {
String name;
TheDemo theDemo;
public TestThread(String name,TheDemo theDemo) {
this.theDemo = theDemo;
this.name = name;
start();
}
@Override
public void run() {
theDemo.test(name);
}
}
public class TheDemo {
public synchronized void test(String name) {
for(int i=0;i<10;i++) {
SOP.print(name + " :: "+i);
try{
Thread.sleep(500);
} catch (Exception e) {
SOP.print(e.getMessage());
}
}
}
public static void main(String[] args) {
TheDemo theDemo = new TheDemo();
new TestThread("THREAD 1",theDemo);
new TestThread("THREAD 2",theDemo);
new TestThread("THREAD 3",theDemo);
}
}
Nota: synchronized
blocca la chiamata del thread successivo al metodo test () a condizione che l'esecuzione del thread precedente non sia terminata. I thread possono accedere a questo metodo uno alla volta. Senza synchronized
tutti i thread è possibile accedere a questo metodo contemporaneamente.
Quando un thread chiama il metodo sincronizzato "test" dell'oggetto (qui oggetto è un'istanza della classe "TheDemo") acquisisce il blocco di quell'oggetto, qualsiasi nuovo thread non può chiamare QUALSIASI metodo sincronizzato dello stesso oggetto fintanto che il thread precedente che aveva acquisito il blocco non rilascia il blocco.
Una cosa simile accade quando viene chiamato un metodo sincronizzato statico della classe. Il thread acquisisce il blocco associato alla classe (in questo caso qualsiasi metodo sincronizzato non statico di un'istanza di quella classe può essere chiamato da qualsiasi thread perché quel blocco a livello di oggetto è ancora disponibile). Qualsiasi altro thread non sarà in grado di chiamare alcun metodo sincronizzato statico della classe fintanto che il blocco a livello di classe non viene rilasciato dal thread che attualmente detiene il blocco.
Uscita sincronizzata
THREAD 1 :: 0
THREAD 1 :: 1
THREAD 1 :: 2
THREAD 1 :: 3
THREAD 1 :: 4
THREAD 1 :: 5
THREAD 1 :: 6
THREAD 1 :: 7
THREAD 1 :: 8
THREAD 1 :: 9
THREAD 3 :: 0
THREAD 3 :: 1
THREAD 3 :: 2
THREAD 3 :: 3
THREAD 3 :: 4
THREAD 3 :: 5
THREAD 3 :: 6
THREAD 3 :: 7
THREAD 3 :: 8
THREAD 3 :: 9
THREAD 2 :: 0
THREAD 2 :: 1
THREAD 2 :: 2
THREAD 2 :: 3
THREAD 2 :: 4
THREAD 2 :: 5
THREAD 2 :: 6
THREAD 2 :: 7
THREAD 2 :: 8
THREAD 2 :: 9
Uscita senza sincronizzazione
THREAD 1 :: 0
THREAD 2 :: 0
THREAD 3 :: 0
THREAD 1 :: 1
THREAD 2 :: 1
THREAD 3 :: 1
THREAD 1 :: 2
THREAD 2 :: 2
THREAD 3 :: 2
THREAD 1 :: 3
THREAD 2 :: 3
THREAD 3 :: 3
THREAD 1 :: 4
THREAD 2 :: 4
THREAD 3 :: 4
THREAD 1 :: 5
THREAD 2 :: 5
THREAD 3 :: 5
THREAD 1 :: 6
THREAD 2 :: 6
THREAD 3 :: 6
THREAD 1 :: 7
THREAD 2 :: 7
THREAD 3 :: 7
THREAD 1 :: 8
THREAD 2 :: 8
THREAD 3 :: 8
THREAD 1 :: 9
THREAD 2 :: 9
THREAD 3 :: 9
synchronized
, ma la coerenza della memoria viene ignorata.
La synchronized
parola chiave impedisce l'accesso simultaneo a un blocco di codice o oggetto da parte di più thread. Tutti i metodi Hashtable
sono synchronized
, quindi solo un thread può eseguirne uno alla volta.
Quando si utilizzano non -build synchronized
come HashMap
, è necessario creare funzioni di sicurezza del thread nel codice per evitare errori di coerenza.
synchronized
significa che in un ambiente multi-thread, un oggetto con synchronized
metodo / i / blocchi / i non consente a due thread di accedere contemporaneamente ai synchronized
metodi / blocchi / i di codice. Ciò significa che un thread non può leggere mentre un altro thread lo aggiorna.
Il secondo thread invece attenderà fino a quando il primo thread non avrà completato la sua esecuzione. Il sovraccarico è la velocità, ma il vantaggio è la coerenza garantita dei dati.
Se l'applicazione è a thread singolo, i synchronized
blocchi non offrono vantaggi.
La synchronized
parola chiave fa sì che un thread ottenga un blocco quando si inserisce il metodo, in modo che solo un thread possa eseguire il metodo contemporaneamente (per la specifica istanza di oggetto, a meno che non sia un metodo statico).
Questo è spesso chiamato rendere la classe thread-safe, ma direi che si tratta di un eufemismo. Mentre è vero che la sincronizzazione protegge lo stato interno del Vector dalla corruzione, ciò di solito non aiuta molto l'utente di Vector.
Considera questo:
if (vector.isEmpty()){
vector.add(data);
}
Anche se i metodi coinvolti sono sincronizzati, poiché vengono bloccati e sbloccati singolarmente, due thread purtroppo temporizzati possono creare un vettore con due elementi.
Quindi, in effetti, è necessario sincronizzare anche il codice dell'applicazione.
Poiché la sincronizzazione a livello di metodo è a) costosa quando non è necessaria eb) insufficiente quando è necessaria la sincronizzazione, ora ci sono sostituzioni non sincronizzate (ArrayList nel caso di Vector).
Più recentemente, il pacchetto di concorrenza è stato rilasciato, con una serie di utility intelligenti che si occupano di problemi multi-threading.
La parola chiave sincronizzata in Java ha a che fare con la sicurezza dei thread, ovvero quando più thread leggono o scrivono la stessa variabile.
Ciò può avvenire direttamente (accedendo alla stessa variabile) o indirettamente (utilizzando una classe che utilizza un'altra classe che accede alla stessa variabile).
La parola chiave sincronizzata viene utilizzata per definire un blocco di codice in cui più thread possono accedere alla stessa variabile in modo sicuro.
Per quanto riguarda la sintassi, la synchronized
parola chiave accetta un Object
come parametro (chiamato oggetto di blocco ), seguito da un { block of code }
.
Quando l'esecuzione rileva questa parola chiave, il thread corrente tenta di "bloccare / acquisire / possedere" (scegli) l' oggetto lock ed eseguire il blocco di codice associato dopo che il blocco è stato acquisito.
Qualsiasi scrittura su variabili all'interno del blocco di codice sincronizzato è garantita per essere visibile a tutti gli altri thread che eseguono allo stesso modo il codice all'interno di un blocco di codice sincronizzato utilizzando lo stesso oggetto di blocco .
Solo un thread alla volta può contenere il blocco, durante il quale tutti gli altri thread che tentano di acquisire lo stesso oggetto di blocco aspetteranno (sospenderne l'esecuzione). Il blocco verrà rilasciato quando l'esecuzione esce dal blocco di codice sincronizzato.
Aggiunta di synchronized
parola chiave per una definizione di metodo è uguale al corpo intero procedimento essendo avvolto in un blocco di codice sincronizzato con l' oggetto di blocco essendo this
(per metodi di istanza) e ClassInQuestion.getClass()
(per metodi di classe) .
- Il metodo di istanza è un metodo che non ha static
parole chiave.
- Il metodo di classe è un metodo che ha una static
parola chiave.
Senza sincronizzazione, non è garantito in quale ordine avvengano le letture e le scritture, lasciando possibilmente la variabile con immondizia.
(Ad esempio una variabile potrebbe finire con la metà dei bit scritti da un thread e la metà dei bit scritti da un altro thread, lasciando la variabile in uno stato che nessuno dei thread ha provato a scrivere, ma un pasticcio combinato di entrambi.)
Non è sufficiente completare un'operazione di scrittura in un thread prima (tempo di clock) un altro thread lo legge, poiché l'hardware potrebbe aver memorizzato nella cache il valore della variabile e il thread di lettura vedrebbe il valore memorizzato nella cache invece di quello su cui è stato scritto esso.
Pertanto, nel caso di Java, è necessario seguire il modello di memoria Java per assicurarsi che non si verifichino errori di threading.
In altre parole: usa la sincronizzazione, le operazioni atomiche o le classi che le usano per te sotto i cofani.
fonti
http://docs.oracle.com/javase/specs/jls/se8/html/index.html
Specifiche del linguaggio Java®, 2015-02-13
Pensalo come una specie di tornello come potresti trovare in un campo da calcio. Ci sono flussi paralleli di persone che vogliono entrare, ma al tornello sono "sincronizzati". Solo una persona alla volta può passare. Tutti quelli che vogliono superare lo faranno, ma potrebbero dover aspettare fino a quando non riescono a passare.
Qual è la parola chiave sincronizzata?
I thread comunicano principalmente condividendo l'accesso ai campi e gli oggetti a cui fanno riferimento i campi di riferimento. Questa forma di comunicazione è estremamente efficiente, ma rende possibili due tipi di errori: interferenza di thread ed errori di coerenza della memoria . Lo strumento necessario per prevenire questi errori è la sincronizzazione.
Blocchi o metodi sincronizzati impediscono l'interferenza del thread e assicurano che i dati siano coerenti. In qualsiasi momento, solo un thread può accedere a un blocco o metodo sincronizzato ( sezione critica ) acquisendo un blocco. Altri thread attenderanno il rilascio del blocco per accedere alla sezione critica .
Quando vengono sincronizzati i metodi?
I metodi vengono sincronizzati quando si aggiunge la synchronized
definizione o la dichiarazione del metodo. Puoi anche sincronizzare un particolare blocco di codice con un metodo.
Cosa significa pro grammaticalmente e logicamente?
Significa che solo un thread può accedere alla sezione critica acquisendo un blocco. A meno che questo thread non rilasci questo lock, tutti gli altri thread dovranno attendere per acquisire un lock. Non hanno accesso per accedere alla sezione critica senza blocco acquisizione.
Questo non può essere fatto con una magia. È responsabilità del programmatore identificare le sezioni critiche nell'applicazione e proteggerle di conseguenza. Java fornisce un framework per proteggere l'applicazione, ma dove e cosa tutte le sezioni da proteggere sono responsabilità del programmatore.
Maggiori dettagli dalla pagina della documentazione di Java
Blocchi intrinseci e sincronizzazione:
La sincronizzazione si basa su un'entità interna nota come blocco intrinseco o blocco monitor. I blocchi intrinseci svolgono un ruolo in entrambi gli aspetti della sincronizzazione: imporre l'accesso esclusivo allo stato di un oggetto e stabilire relazioni che accadono prima che siano essenziali per la visibilità.
A ogni oggetto è associato un blocco intrinseco . Per convenzione, un thread che necessita di un accesso esclusivo e coerente ai campi di un oggetto deve acquisire il blocco intrinseco dell'oggetto prima di accedervi, quindi rilasciare il blocco intrinseco una volta terminato con essi.
Si dice che un thread possiede il blocco intrinseco tra il momento in cui ha acquisito il blocco e rilasciato il blocco. Finché un thread possiede un blocco intrinseco, nessun altro thread può acquisire lo stesso blocco. L'altro thread si bloccherà quando tenta di acquisire il blocco.
Quando un thread rilascia un blocco intrinseco, viene stabilita una relazione che precede tra quell'azione e qualsiasi successiva acquisizione dello stesso blocco.
Rendere sincronizzati i metodi ha due effetti :
In primo luogo, non è possibile interlacciare due invocazioni di metodi sincronizzati sullo stesso oggetto.
Quando un thread esegue un metodo sincronizzato per un oggetto, tutti gli altri thread che invocano metodi sincronizzati per lo stesso blocco di oggetti (sospensione dell'esecuzione) fino a quando il primo thread non viene eseguito con l'oggetto.
In secondo luogo, quando esce un metodo sincronizzato, stabilisce automaticamente una relazione prima dell'evocazione successiva di un metodo sincronizzato per lo stesso oggetto.
Ciò garantisce che le modifiche allo stato dell'oggetto siano visibili a tutti i thread.
Cerca altre alternative alla sincronizzazione in:
Synchronized normal method
equivalente a
Synchronized statement
(utilizzare questo)
class A {
public synchronized void methodA() {
// all function code
}
equivalent to
public void methodA() {
synchronized(this) {
// all function code
}
}
}
Synchronized static method
equivalente a Synchronized statement
(classe d'uso)
class A {
public static synchronized void methodA() {
// all function code
}
equivalent to
public void methodA() {
synchronized(A.class) {
// all function code
}
}
}
Istruzione sincronizzata (usando la variabile)
class A {
private Object lock1 = new Object();
public void methodA() {
synchronized(lock1 ) {
// all function code
}
}
}
Per synchronized
, abbiamo entrambi Synchronized Methods
e Synchronized Statements
. Tuttavia, Synchronized Methods
è simile a quello che Synchronized Statements
dobbiamo solo capire Synchronized Statements
.
=> Fondamentalmente, avremo
synchronized(object or class) { // object/class use to provides the intrinsic lock
// code
}
Ecco 2 pensa che aiutano a capire synchronized
intrinsic lock
associato con esso.synchronized statement
, acquisisce automaticamente l' oggetto intrinsic lock
per synchronized statement's
quell'oggetto e lo rilascia quando il metodo ritorna. Finché un thread possiede un thread intrinsic lock
, NESSUN altro thread può acquisire il SAME lock => thread safe.=> Quando un thread A
invoke synchronized(this){// code 1}
=> tutto il codice di blocco (all'interno della classe) dove have synchronized(this)
e all synchronized normal method
(all'interno della classe) è bloccato perché SAME lock. Verrà eseguito dopo lo thread A
sblocco ("// code 1" terminato).
Questo comportamento è simile a synchronized(a variable){// code 1}
o synchronized(class)
.
SAME LOCK => lock (non dipende da quale metodo? O quali istruzioni?)
Preferisco synchronized statements
perché è più allungabile. Esempio, in futuro, devi solo sincronizzare una parte del metodo. Esempio, hai 2 metodi sincronizzati e non sono rilevanti l'uno per l'altro, tuttavia quando un thread esegue un metodo, bloccherà l'altro metodo (può impedirlo con l'uso synchronized(a variable)
).
Tuttavia, applicare il metodo sincronizzato è semplice e il codice sembra semplice. Per alcune classi, esiste solo 1 metodo sincronizzato o tutti i metodi sincronizzati nella classe in relazione reciproca => che possiamo usare synchronized method
per rendere il codice più breve e facile da capire
(non è rilevante per molto synchronized
, è la differenza tra oggetto e classe o nessuna-statica e statica).
synchronized
il metodo normale o synchronized(this)
oppure synchronized(non-static variable)
si sincronizzerà sulla base di ogni istanza di oggetto. synchronized
un metodo statico synchronized(class)
o synchronized(static variable)
si sincronizzerà in base alla classehttps://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html
Spero che sia d'aiuto
Ecco una spiegazione da The Java Tutorials .
Considera il seguente codice:
public class SynchronizedCounter { private int c = 0; public synchronized void increment() { c++; } public synchronized void decrement() { c--; } public synchronized int value() { return c; } }
if
count
è un'istanza diSynchronizedCounter
, quindi rendere sincronizzati questi metodi ha due effetti:
- In primo luogo, non è possibile interlacciare due invocazioni di metodi sincronizzati sullo stesso oggetto. Quando un thread esegue un metodo sincronizzato per un oggetto, tutti gli altri thread che invocano metodi sincronizzati per lo stesso blocco di oggetti (sospensione dell'esecuzione) fino a quando il primo thread non viene eseguito con l'oggetto.
- In secondo luogo, quando esce un metodo sincronizzato, stabilisce automaticamente una relazione prima dell'evocazione successiva di un metodo sincronizzato per lo stesso oggetto. Ciò garantisce che le modifiche allo stato dell'oggetto siano visibili a tutti i thread.
Per la mia comprensione sincronizzato significa sostanzialmente che il compilatore scrive monitor.enter e monitor.exit attorno al tuo metodo. In quanto tale, può essere thread-safe a seconda di come viene utilizzato (ciò che intendo è che puoi scrivere un oggetto con metodi sincronizzati che non è thread-safe a seconda di ciò che fa la tua classe).
Quello che manca alle altre risposte è un aspetto importante: le barriere della memoria . La sincronizzazione dei thread è essenzialmente composta da due parti: serializzazione e visibilità. Consiglio a tutti di google di "jvm memory barrier", in quanto si tratta di un argomento non banale ed estremamente importante (se si modificano i dati condivisi a cui accedono più thread). Dopo averlo fatto, consiglio di guardare le classi del pacchetto java.util.concurrent che aiutano a evitare l'uso della sincronizzazione esplicita, che a sua volta aiuta a mantenere i programmi semplici ed efficienti, forse anche a prevenire i deadlock.
Uno di questi esempi è ConcurrentLinkedDeque . Insieme al modello di comando consente di creare thread di lavoro altamente efficienti inserendo i comandi nella coda simultanea: nessuna sincronizzazione esplicita necessaria, nessun deadlock possibile, nessun sonno esplicito () necessario, basta eseguire il polling della coda chiamando take ().
In breve: si verifica la "sincronizzazione della memoria" implicitamente quando si avvia un thread, un thread termina, si legge una variabile volatile, si sblocca un monitor (si lascia un blocco / funzione sincronizzato) ecc. Questa "sincronizzazione" influisce (in un certo senso "arrossisce ") tutte le scritture fatte prima di quella particolare azione. Nel caso del summenzionato ConcurrentLinkedDeque , la documentazione "dice":
Effetti di coerenza della memoria: come con altre raccolte simultanee, le azioni in un thread prima di posizionare un oggetto in un ConcurrentLinkedDeque avvengono prima delle azioni successive all'accesso o alla rimozione di quell'elemento dal ConcurrentLinkedDeque in un altro thread.
Questo comportamento implicito è un aspetto un po 'pernicioso perché la maggior parte dei programmatori Java senza molta esperienza prenderà molto come dato a causa di esso. E poi improvvisamente inciampare su questo thread dopo che Java non sta facendo quello che dovrebbe "fare" in produzione dove c'è un carico di lavoro diverso - ed è piuttosto difficile testare i problemi di concorrenza.
Sincronizzato significa semplicemente che più thread se associati a un singolo oggetto possono impedire la lettura e la scrittura sporche se si utilizza un blocco sincronizzato su un oggetto particolare. Per darti maggiore chiarezza, facciamo un esempio:
class MyRunnable implements Runnable {
int var = 10;
@Override
public void run() {
call();
}
public void call() {
synchronized (this) {
for (int i = 0; i < 4; i++) {
var++;
System.out.println("Current Thread " + Thread.currentThread().getName() + " var value "+var);
}
}
}
}
public class MutlipleThreadsRunnable {
public static void main(String[] args) {
MyRunnable runnable1 = new MyRunnable();
MyRunnable runnable2 = new MyRunnable();
Thread t1 = new Thread(runnable1);
t1.setName("Thread -1");
Thread t2 = new Thread(runnable2);
t2.setName("Thread -2");
Thread t3 = new Thread(runnable1);
t3.setName("Thread -3");
t1.start();
t2.start();
t3.start();
}
}
Abbiamo creato due oggetti classe MyRunnable, runnable1 condiviso con thread 1 e thread 3 e runnable2 condiviso solo con thread 2. Ora, quando t1 e t3 si avviano senza l'uso della sincronizzazione, l'output PFB suggerisce che entrambi i thread 1 e 3 influenzano simultaneamente il valore var dove per il thread 2, var ha la propria memoria.
Without Synchronized keyword
Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -2 var value 12
Current Thread Thread -2 var value 13
Current Thread Thread -2 var value 14
Current Thread Thread -1 var value 12
Current Thread Thread -3 var value 13
Current Thread Thread -3 var value 15
Current Thread Thread -1 var value 14
Current Thread Thread -1 var value 17
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 18
Utilizzando Synchronzied, thread 3 in attesa del completamento del thread 1 in tutti gli scenari. Sono stati acquisiti due blocchi, uno su runnable1 condiviso dal thread 1 e thread 3 e l'altro su runnable2 condiviso solo dal thread 2.
Current Thread Thread -1 var value 11
Current Thread Thread -2 var value 11
Current Thread Thread -1 var value 12
Current Thread Thread -2 var value 12
Current Thread Thread -1 var value 13
Current Thread Thread -2 var value 13
Current Thread Thread -1 var value 14
Current Thread Thread -2 var value 14
Current Thread Thread -3 var value 15
Current Thread Thread -3 var value 16
Current Thread Thread -3 var value 17
Current Thread Thread -3 var value 18
sincronizzato semplice significa che due thread non possono accedere contemporaneamente al blocco / metodo. Quando diciamo che qualsiasi blocco / metodo di una classe è sincronizzato significa che solo un thread alla volta può accedervi. Internamente il thread che tenta di accedervi per primo prende un lock su quell'oggetto e fintanto che questo lock non è disponibile nessun altro thread può accedere a nessuno dei metodi / blocchi sincronizzati di quell'istanza della classe.
Nota un altro thread può accedere a un metodo dello stesso oggetto che non è definito come sincronizzato. Un thread può rilasciare il blocco chiamando
Object.wait()
synchronized
blocco in Java è un monitor in multithreading. synchronized
il blocco con lo stesso oggetto / classe può essere eseguito da un solo thread, tutti gli altri sono in attesa. Può essere d'aiuto in race condition
situazioni in cui diversi thread tentano di aggiornare la stessa variabile (il primo passo utilizza volatile
Informazioni )
Java 5
esteso synchronized
supportando happens-before
[Informazioni]
Si verifica uno sblocco (blocco sincronizzato o uscita del metodo) di un monitor prima di ogni blocco successivo (blocco sincronizzato o immissione del metodo) dello stesso monitor.
Il prossimo passo è java.util.concurrent