Qualcuno può dirmi il vantaggio del metodo sincronizzato rispetto al blocco sincronizzato con un esempio?
Qualcuno può dirmi il vantaggio del metodo sincronizzato rispetto al blocco sincronizzato con un esempio?
Risposte:
Qualcuno può dirmi il vantaggio del metodo sincronizzato rispetto al blocco sincronizzato con un esempio? Grazie.
Non esiste un chiaro vantaggio dell'utilizzo del metodo sincronizzato sul blocco.
Forse l'unico (ma non lo definirei un vantaggio) è che non è necessario includere il riferimento all'oggetto this
.
Metodo:
public synchronized void method() { // blocks "this" from here....
...
...
...
} // to here
Bloccare:
public void method() {
synchronized( this ) { // blocks "this" from here ....
....
....
....
} // to here...
}
Vedere? Nessun vantaggio.
I blocchi non hanno vantaggi rispetto ai metodi, però, per lo più in termini di flessibilità, perché è possibile utilizzare un altro oggetto come blocco, mentre la sincronizzazione il metodo sarebbe bloccare l'intero oggetto.
Confrontare:
// locks the whole object
...
private synchronized void someInputRelatedWork() {
...
}
private synchronized void someOutputRelatedWork() {
...
}
vs.
// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();
private void someInputRelatedWork() {
synchronized(inputLock) {
...
}
}
private void someOutputRelatedWork() {
synchronized(outputLock) {
...
}
}
Inoltre, se il metodo aumenta, puoi comunque mantenere separata la sezione sincronizzata:
private void method() {
... code here
... code here
... code here
synchronized( lock ) {
... very few lines of code here
}
... code here
... code here
... code here
... code here
}
synchronized
blocco viene implementato utilizzando due istruzioni monitorenter
e monitorexit
, oltre a un gestore di eccezioni, che assicura che monitorexit
venga chiamato anche in casi eccezionali. Questo è tutto salvato quando si utilizza un synchronized
metodo.
L'unica vera differenza è che un blocco sincronizzato può scegliere su quale oggetto sincronizzare. Un metodo sincronizzato può utilizzare solo 'this'
(o l'istanza di classe corrispondente per un metodo di classe sincronizzato). Ad esempio, questi sono semanticamente equivalenti:
synchronized void foo() {
...
}
void foo() {
synchronized (this) {
...
}
}
Quest'ultimo è più flessibile poiché può competere per il blocco associato di qualsiasi oggetto, spesso una variabile membro. È anche più granulare perché potresti avere l'esecuzione simultanea di codice prima e dopo il blocco ma comunque all'interno del metodo. Naturalmente, è possibile utilizzare altrettanto facilmente un metodo sincronizzato rifattorizzando il codice simultaneo in metodi separati non sincronizzati. Usa qualunque cosa renda il codice più comprensibile.
Professionisti:
Contro:
Professionisti:
Contro:
Personalmente preferisco usare metodi sincronizzati con classi focalizzate solo sulla cosa che necessita di sincronizzazione. Tale classe dovrebbe essere il più piccola possibile e quindi dovrebbe essere facile rivedere la sincronizzazione. Altri non dovrebbero preoccuparsi della sincronizzazione.
La differenza principale è che se si utilizza un blocco sincronizzato è possibile bloccare un oggetto diverso da questo, il che consente di essere molto più flessibile.
Supponiamo di avere una coda di messaggi e più produttori e consumatori di messaggi. Non vogliamo che i produttori interferiscano l'uno con l'altro, ma i consumatori dovrebbero essere in grado di recuperare i messaggi senza dover aspettare i produttori. Quindi creiamo solo un oggetto
Object writeLock = new Object();
E d'ora in poi ogni volta che un produttore vuole aggiungere un nuovo messaggio, ci limitiamo a bloccarlo:
synchronized(writeLock){
// do something
}
Quindi i consumatori possono ancora leggere e i produttori saranno bloccati.
Metodo sincronizzato
I metodi sincronizzati hanno due effetti.
In primo luogo, 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.
Si noti che i costruttori non possono essere sincronizzati: l'uso della parola chiave sincronizzata con un costruttore è un errore di sintassi. Sincronizzare i costruttori non ha senso, perché solo il thread che crea un oggetto dovrebbe avervi accesso mentre viene costruito.
Dichiarazione sincronizzata
A differenza dei metodi sincronizzati, le istruzioni sincronizzate devono specificare l'oggetto che fornisce il blocco intrinseco: il più delle volte lo utilizzo per sincronizzare l'accesso a un elenco o una mappa ma non voglio bloccare l'accesso a tutti i metodi dell'oggetto.
D: Blocchi intrinseci e sincronizzazione La sincronizzazione si basa su un'entità interna nota come blocco intrinseco o blocco monitor. (Le specifiche API spesso si riferiscono a questa entità semplicemente come un "monitor".) I blocchi intrinsechi 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.
package test;
public class SynchTest implements Runnable {
private int c = 0;
public static void main(String[] args) {
new SynchTest().test();
}
public void test() {
// Create the object with the run() method
Runnable runnable = new SynchTest();
Runnable runnable2 = new SynchTest();
// Create the thread supplying it with the runnable object
Thread thread = new Thread(runnable,"thread-1");
Thread thread2 = new Thread(runnable,"thread-2");
// Here the key point is passing same object, if you pass runnable2 for thread2,
// then its not applicable for synchronization test and that wont give expected
// output Synchronization method means "it is not possible for two invocations
// of synchronized methods on the same object to interleave"
// Start the thread
thread.start();
thread2.start();
}
public synchronized void increment() {
System.out.println("Begin thread " + Thread.currentThread().getName());
System.out.println(this.hashCode() + "Value of C = " + c);
// If we uncomment this for synchronized block, then the result would be different
// synchronized(this) {
for (int i = 0; i < 9999999; i++) {
c += i;
}
// }
System.out.println("End thread " + Thread.currentThread().getName());
}
// public synchronized void decrement() {
// System.out.println("Decrement " + Thread.currentThread().getName());
// }
public int value() {
return c;
}
@Override
public void run() {
this.increment();
}
}
Controllo incrociato di diverse uscite con metodo sincronizzato, blocco e senza sincronizzazione.
Nota: metodi e blocchi sincronizzati statici funzionano sull'oggetto Class.
public class MyClass {
// locks MyClass.class
public static synchronized void foo() {
// do something
}
// similar
public static void foo() {
synchronized(MyClass.class) {
// do something
}
}
}
Quando il compilatore java converte il codice sorgente in codice byte, gestisce i metodi sincronizzati e i blocchi sincronizzati in modo molto diverso.
Quando la JVM esegue un metodo sincronizzato, il thread in esecuzione identifica che la struttura method_info del metodo ha il flag ACC_SYNCHRONIZED impostato, quindi acquisisce automaticamente il blocco dell'oggetto, chiama il metodo e rilascia il blocco. Se si verifica un'eccezione, il thread rilascia automaticamente il blocco.
La sincronizzazione di un blocco di metodo, d'altro canto, ignora il supporto integrato della JVM per l'acquisizione del blocco di un oggetto e della gestione delle eccezioni e richiede che la funzionalità sia esplicitamente scritta nel codice byte. Se leggi il codice byte per un metodo con un blocco sincronizzato, vedrai più di una dozzina di operazioni aggiuntive per gestire questa funzionalità.
Questo mostra le chiamate per generare sia un metodo sincronizzato che un blocco sincronizzato:
public class SynchronizationExample {
private int i;
public synchronized int synchronizedMethodGet() {
return i;
}
public int synchronizedBlockGet() {
synchronized( this ) {
return i;
}
}
}
Il synchronizedMethodGet()
metodo genera il seguente codice byte:
0: aload_0
1: getfield
2: nop
3: iconst_m1
4: ireturn
Ed ecco il codice byte dal synchronizedBlockGet()
metodo:
0: aload_0
1: dup
2: astore_1
3: monitorenter
4: aload_0
5: getfield
6: nop
7: iconst_m1
8: aload_1
9: monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow
Una differenza significativa tra il metodo sincronizzato e il blocco è che, il blocco sincronizzato generalmente riduce l'ambito del blocco. Poiché l'ambito del blocco è inversamente proporzionale alle prestazioni, è sempre meglio bloccare solo una sezione critica del codice. Uno dei migliori esempi di utilizzo del blocco sincronizzato è il doppio controllo bloccato nel modello Singleton dove invece di bloccare l'intero getInstance()
metodo blocciamo solo la sezione critica del codice che viene utilizzata per creare l'istanza Singleton. Ciò migliora drasticamente le prestazioni perché il blocco è richiesto solo una o due volte.
Durante l'utilizzo di metodi sincronizzati, è necessario prestare particolare attenzione se si mescolano metodi sincronizzati statici e non sincronizzati.
monitorenter
e monitorexit
prima di eseguire il codice.
Molto spesso lo uso per sincronizzare l'accesso a un elenco o una mappa ma non voglio bloccare l'accesso a tutti i metodi dell'oggetto.
Nel codice seguente un thread che modifica l'elenco non bloccherà l'attesa di un thread che sta modificando la mappa. Se i metodi fossero sincronizzati sull'oggetto, ciascun metodo dovrebbe attendere anche se le modifiche che stanno apportando non sarebbero in conflitto.
private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();
public void put( String s, Bar b ) {
synchronized( myMap ) {
myMap.put( s,b );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public void hasKey( String s, ) {
synchronized( myMap ) {
myMap.hasKey( s );
}
}
public void add( Foo f ) {
synchronized( myList ) {
myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
}
}
public Thing getMedianFoo() {
Foo med = null;
synchronized( myList ) {
Collections.sort(myList);
med = myList.get(myList.size()/2);
}
return med;
}
Con i blocchi sincronizzati, puoi avere più sincronizzatori, in modo che più cose simultanee ma non in conflitto possano continuare contemporaneamente.
I metodi sincronizzati possono essere controllati utilizzando l'API di reflection. Ciò può essere utile per testare alcuni contratti, ad esempio tutti i metodi nel modello sono sincronizzati .
Il frammento seguente stampa tutti i metodi sincronizzati di Hashtable:
for (Method m : Hashtable.class.getMethods()) {
if (Modifier.isSynchronized(m.getModifiers())) {
System.out.println(m);
}
}
Nota importante sull'uso del blocco sincronizzato: fai attenzione a cosa usi come oggetto lock!
Lo snippet di codice di user2277816 sopra illustra questo punto in quanto un riferimento a un valore letterale di stringa viene utilizzato come oggetto di blocco. Comprendi che i valori letterali delle stringhe vengono automaticamente internati in Java e dovresti iniziare a vedere il problema: ogni pezzo di codice che si sincronizza sul "blocco" letterale, condivide lo stesso blocco! Questo può facilmente portare a deadlock con pezzi di codice completamente non correlati.
Non è solo con gli oggetti String che devi stare attento. Anche le primitive inscatolate sono un pericolo, poiché l'autoboxing e i metodi valueOf possono riutilizzare gli stessi oggetti, a seconda del valore.
Per ulteriori informazioni, consultare: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused
Spesso l'utilizzo di un blocco a livello di metodo è troppo scortese. Perché bloccare un pezzo di codice che non accede a nessuna risorsa condivisa bloccando un intero metodo. Poiché ogni oggetto ha un blocco, è possibile creare oggetti fittizi per implementare la sincronizzazione a livello di blocco. Il livello di blocco è più efficiente perché non blocca l'intero metodo.
Ecco qualche esempio
Livello del metodo
class MethodLevel {
//shared among threads
SharedResource x, y ;
public void synchronized method1() {
//multiple threads can't access
}
public void synchronized method2() {
//multiple threads can't access
}
public void method3() {
//not synchronized
//multiple threads can access
}
}
Livello di blocco
class BlockLevel {
//shared among threads
SharedResource x, y ;
//dummy objects for locking
Object xLock = new Object();
Object yLock = new Object();
public void method1() {
synchronized(xLock){
//access x here. thread safe
}
//do something here but don't use SharedResource x, y
// because will not be thread-safe
synchronized(xLock) {
synchronized(yLock) {
//access x,y here. thread safe
}
}
//do something here but don't use SharedResource x, y
//because will not be thread-safe
}//end of method1
}
[Modificare]
Per Collection
come Vector
e Hashtable
sono sincronizzati quando ArrayList
o HashMap
non lo sono e devi impostare una parola chiave sincronizzata o invocare il metodo sincronizzato Collezioni:
Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
L'unica differenza: i blocchi sincronizzati consentono il blocco granulare a differenza del metodo sincronizzato
Fondamentalmente synchronized
blocco o metodi sono stati usati per scrivere il codice thread-safe evitando errori di incoerenza della memoria.
Questa domanda è molto antica e molte cose sono state cambiate negli ultimi 7 anni. Sono stati introdotti nuovi costrutti di programmazione per la sicurezza del thread.
Puoi ottenere la sicurezza dei thread utilizzando l'API di concorrenza avanzata anziché i synchronied
blocchi. Questa pagina di documentazione fornisce buoni costrutti di programmazione per ottenere la sicurezza del thread.
Lock Objects supporta i modi di bloccare che semplificano molte applicazioni simultanee.
I dirigenti definiscono un'API di alto livello per l'avvio e la gestione dei thread. Le implementazioni di Executor fornite da java.util.concurrent forniscono la gestione del pool di thread adatta per applicazioni su larga scala.
Le raccolte simultanee semplificano la gestione di grandi raccolte di dati e possono ridurre notevolmente la necessità di sincronizzazione.
Le variabili atomiche hanno funzionalità che riducono al minimo la sincronizzazione e aiutano a evitare errori di coerenza della memoria.
ThreadLocalRandom (in JDK 7) fornisce una generazione efficiente di numeri pseudocasuali da più thread.
Una migliore sostituzione per sincronizzato è ReentrantLock , che utilizza l' Lock
API
Un blocco di esclusione reciproca rientrante con lo stesso comportamento di base e semantica del blocco di monitoraggio implicito a cui si accede utilizzando metodi e istruzioni sincronizzati, ma con funzionalità estese.
Esempio con blocchi:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
Consultare anche i pacchetti java.util.concurrent e java.util.concurrent.atomic per altri costrutti di programmazione.
Fare riferimento anche a questa domanda correlata:
In generale, questi sono per lo più diversi dall'essere espliciti sul monitor dell'oggetto utilizzato rispetto all'implicito oggetto. Un aspetto negativo dei metodi sincronizzati che a volte penso sia trascurato è che nell'usare il riferimento "this" per la sincronizzazione su di te si lascia aperta la possibilità che gli oggetti esterni si blocchino sullo stesso oggetto. Questo può essere un bug molto sottile se ci si imbatte. La sincronizzazione su un oggetto esplicito interno o su un altro campo esistente può evitare questo problema, incapsulando completamente la sincronizzazione.
Come già detto qui, il blocco sincronizzato può utilizzare la variabile definita dall'utente come oggetto di blocco, quando la funzione sincronizzata utilizza solo "questo". E ovviamente puoi manipolare aree della tua funzione che dovrebbero essere sincronizzate. Ma tutti dicono che nessuna differenza tra funzione sincronizzata e blocco che copre l'intera funzione usando "questo" come oggetto di blocco. Ciò non è vero, la differenza è nel codice byte che verrà generato in entrambe le situazioni. In caso di utilizzo del blocco sincronizzato dovrebbe essere allocata la variabile locale che contiene riferimenti a "this". E di conseguenza avremo dimensioni un po 'più grandi per la funzione (non rilevante se hai solo un numero limitato di funzioni).
Una spiegazione più dettagliata della differenza è possibile trovare qui: http://www.artima.com/insidejvm/ed2/threadsynchP.html
Nel caso di metodi sincronizzati, il blocco verrà acquisito su un oggetto. Ma se vai con il blocco sincronizzato hai un'opzione per specificare un oggetto su cui verrà acquisito il blocco.
Esempio :
Class Example {
String test = "abc";
// lock will be acquired on String test object.
synchronized (test) {
// do something
}
lock will be acquired on Example Object
public synchronized void testMethod() {
// do some thing
}
}
So che questa è una vecchia domanda, ma con la mia rapida lettura delle risposte qui, non ho davvero visto nessuno menzionare che a volte un synchronized
metodo potrebbe essere il blocco sbagliato .
Da Java Concurrency In Practice (pag. 72):
public class ListHelper<E> {
public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...
public syncrhonized boolean putIfAbsent(E x) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
Il codice precedente ha l' aspetto di essere thread-safe. Tuttavia, in realtà non lo è. In questo caso il blocco si ottiene sull'istanza della classe. Tuttavia, è possibile che l' elenco venga modificato da un altro thread che non utilizza quel metodo. L'approccio corretto sarebbe usare
public boolean putIfAbsent(E x) {
synchronized(list) {
boolean absent = !list.contains(x);
if(absent) {
list.add(x);
}
return absent;
}
}
Il codice precedente bloccherebbe tutti i thread che tentano di modificare l' elenco dalla modifica dell'elenco fino al completamento del blocco sincronizzato.
List
può causare problemi di prestazioni se esiste un registro di codice che non deve necessariamente essere sincronizzato
In pratica, il vantaggio dei metodi sincronizzati rispetto ai blocchi sincronizzati è che sono più resistenti agli idioti; poiché non puoi scegliere un oggetto arbitrario da bloccare, non puoi usare in modo improprio la sintassi del metodo sincronizzato per fare cose stupide come bloccare letteralmente una stringa o bloccare il contenuto di un campo mutabile che viene modificato da sotto i thread.
D'altra parte, con i metodi sincronizzati non è possibile proteggere il blocco dall'acquisizione da parte di alcun thread in grado di ottenere un riferimento all'oggetto.
Quindi usare la sincronizzazione come modificatore sui metodi è meglio per proteggere i tuoi cow-orker da farsi male, mentre usare i blocchi sincronizzati in combinazione con oggetti di blocco finali privati è meglio per proteggere il tuo codice dai cow-orker.
Da un riepilogo delle specifiche Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html
L'istruzione sincronizzata (§14.17) calcola un riferimento a un oggetto; tenta quindi di eseguire un'azione di blocco su quell'oggetto e non procede ulteriormente fino al completamento dell'azione di blocco. ...
Un metodo sincronizzato (§8.4.3.5) esegue automaticamente un'azione di blocco quando viene invocato; il suo corpo non viene eseguito fino a quando l'azione di blocco non è stata completata con successo. Se il metodo è un metodo di istanza , blocca il blocco associato all'istanza per la quale è stato invocato (ovvero, l'oggetto che sarà noto come questo durante l'esecuzione del corpo del metodo). Se il metodo è statico , blocca il blocco associato all'oggetto Class che rappresenta la classe in cui è definito il metodo. ...
Sulla base di queste descrizioni, direi che la maggior parte delle risposte precedenti sono corrette e che un metodo sincronizzato potrebbe essere particolarmente utile per i metodi statici, dove altrimenti dovresti capire come ottenere l'oggetto "Class che rappresenta la classe in cui era il metodo definito."
Modifica: inizialmente pensavo fossero citazioni delle specifiche Java attuali. Chiarito che questa pagina è solo un sommario / spiegazione delle specifiche
TLDR; Né utilizzare il synchronized
modificatore né l' synchronized(this){...}
espressione ma synchronized(myLock){...}
dove si myLock
trova un campo dell'istanza finale che contiene un oggetto privato.
La differenza tra l'uso del synchronized
modificatore nella dichiarazione del metodo e l' synchronized(..){ }
espressione nel corpo del metodo sono queste:
synchronized
modificatore specificato nella firma del metodo
synchronized(this) { .... }
, ethis
oggetto come blocco quando dichiarato su un metodo non statico o la classe che lo racchiude quando dichiarato su un metodo statico.synchronized(...){...}
espressione ti consente
Tuttavia, l'utilizzo del synchronized
modificatore o synchronized(...) {...}
con this
come oggetto lock (come in synchronized(this) {...}
) presenta lo stesso svantaggio. Entrambi usano la propria istanza come oggetto di blocco per la sincronizzazione. Questo è pericoloso perché non solo l'oggetto stesso ma qualsiasi altro oggetto / codice esterno che contiene un riferimento a quell'oggetto può anche usarlo come blocco di sincronizzazione con effetti collaterali potenzialmente gravi (degrado delle prestazioni e deadlock ).
Pertanto, la migliore pratica non è utilizzare né il synchronized
modificatore né l' synchronized(...)
espressione insieme this
come oggetto lock ma un oggetto lock privato di questo oggetto. Per esempio:
public class MyService {
private final lock = new Object();
public void doThis() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
public void doThat() {
synchronized(lock) {
// do code that requires synchronous execution
}
}
}
È inoltre possibile utilizzare più oggetti di blocco, ma è necessario prestare particolare attenzione per garantire che ciò non comporti deadlock quando utilizzato nidificato.
public class MyService {
private final lock1 = new Object();
private final lock2 = new Object();
public void doThis() {
synchronized(lock1) {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThat() and doMore().
}
}
public void doThat() {
synchronized(lock1) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doMore() may execute concurrently
}
}
public void doMore() {
synchronized(lock2) {
// code here is guaranteed not to be executes at the same time
// as the synchronized code in doThis().
// doThat() may execute concurrently
}
}
}
Suppongo che questa domanda riguardi la differenza tra Thread Safe Singleton e l' inizializzazione Lazy con doppio controllo di blocco . Mi riferisco sempre a questo articolo quando devo implementare alcuni singleton specifici.
Bene, questo è un thread sicuro Singleton :
// Java program to create Thread Safe
// Singleton class
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
//synchronized method to control simultaneous access
synchronized public static GFG getInstance()
{
if (instance == null)
{
// if instance is null, initialize
instance = new GFG();
}
return instance;
}
}
Professionisti:
L'inizializzazione lenta è possibile.
È thread-safe.
Contro:
- Il metodo getInstance () è sincronizzato, quindi causa prestazioni lente poiché più thread non possono accedervi contemporaneamente.
Questa è un'inizializzazione Lazy con blocco doppio controllo :
// Java code to explain double check locking
public class GFG
{
// private instance, so that it can be
// accessed by only by getInstance() method
private static GFG instance;
private GFG()
{
// private constructor
}
public static GFG getInstance()
{
if (instance == null)
{
//synchronized block to remove overhead
synchronized (GFG.class)
{
if(instance==null)
{
// if instance is null, initialize
instance = new GFG();
}
}
}
return instance;
}
}
Professionisti:
L'inizializzazione lenta è possibile.
È anche sicuro per i thread.
Le prestazioni ridotte a causa della parola chiave sincronizzata vengono superate.
Contro:
La prima volta, può influire sulle prestazioni.
Come contro. del metodo di blocco a doppio controllo è sopportabile, quindi può essere utilizzato per applicazioni multi-thread ad alte prestazioni.
Si prega di fare riferimento a questo articolo per maggiori dettagli:
https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/
Sincronizzazione con i thread. 1) MAI usare sincronizzato (questo) in un thread che non funziona. La sincronizzazione con (questo) utilizza il thread corrente come oggetto thread di blocco. Poiché ogni thread è indipendente dagli altri thread, NON esiste alcun coordinamento della sincronizzazione. 2) Test di codice mostrano che in Java 1.6 su un Mac la sincronizzazione del metodo non funziona. 3) sincronizzato (lockObj) dove lockObj è un oggetto condiviso comune di tutti i thread che si sincronizzano su di esso funzionerà. 4) ReenterantLock.lock () e .unlock () funzionano. Vedi tutorial Java per questo.
Il codice seguente mostra questi punti. Contiene anche il vettore thread-safe che verrebbe sostituito da ArrayList, per mostrare che molti thread che si aggiungono a un Vector non perdono alcuna informazione, mentre lo stesso con un ArrayList può perdere informazioni. 0) Il codice corrente mostra la perdita di informazioni a causa delle condizioni di gara A) Commenta la linea A attualmente etichettata, e decommenta la linea A sopra di essa, quindi esegui, il metodo perde i dati ma non dovrebbe. B) Invertire il passaggio A, decommentare B e // end block}. Quindi esegui per vedere i risultati senza perdita di dati. C) Commenta B, decommenta C. Esegui, vedi la sincronizzazione su (questo) perde i dati, come previsto. Non ho tempo per completare tutte le varianti, spero che questo aiuti. Se la sincronizzazione è attiva (presente) o la sincronizzazione del metodo funziona, indicare quale versione di Java e OS è stata testata. Grazie.
import java.util.*;
/** RaceCondition - Shows that when multiple threads compete for resources
thread one may grab the resource expecting to update a particular
area but is removed from the CPU before finishing. Thread one still
points to that resource. Then thread two grabs that resource and
completes the update. Then thread one gets to complete the update,
which over writes thread two's work.
DEMO: 1) Run as is - see missing counts from race condition, Run severa times, values change
2) Uncomment "synchronized(countLock){ }" - see counts work
Synchronized creates a lock on that block of code, no other threads can
execute code within a block that another thread has a lock.
3) Comment ArrayList, unComment Vector - See no loss in collection
Vectors work like ArrayList, but Vectors are "Thread Safe"
May use this code as long as attribution to the author remains intact.
/mf
*/
public class RaceCondition {
private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
// private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)
private String countLock="lock"; // Object use for locking the raceCount
private int raceCount = 0; // simple add 1 to this counter
private int MAX = 10000; // Do this 10,000 times
private int NUM_THREADS = 100; // Create 100 threads
public static void main(String [] args) {
new RaceCondition();
}
public RaceCondition() {
ArrayList<Thread> arT = new ArrayList<Thread>();
// Create thread objects, add them to an array list
for( int i=0; i<NUM_THREADS; i++){
Thread rt = new RaceThread( ); // i );
arT.add( rt );
}
// Start all object at once.
for( Thread rt : arT ){
rt.start();
}
// Wait for all threads to finish before we can print totals created by threads
for( int i=0; i<NUM_THREADS; i++){
try { arT.get(i).join(); }
catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
}
// All threads finished, print the summary information.
// (Try to print this informaiton without the join loop above)
System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
MAX*NUM_THREADS, raceList.size(), raceCount );
System.out.printf("Array lost %,d. Count lost %,d\n",
MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
} // end RaceCondition constructor
class RaceThread extends Thread {
public void run() {
for ( int i=0; i<MAX; i++){
try {
update( i );
} // These catches show when one thread steps on another's values
catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
catch( OutOfMemoryError oome ) { System.out.print("O"); }
}
}
// so we don't lose counts, need to synchronize on some object, not primitive
// Created "countLock" to show how this can work.
// Comment out the synchronized and ending {, see that we lose counts.
// public synchronized void update(int i){ // use A
public void update(int i){ // remove this when adding A
// synchronized(countLock){ // or B
// synchronized(this){ // or C
raceCount = raceCount + 1;
raceList.add( i ); // use Vector
// } // end block for B or C
} // end update
} // end RaceThread inner class
} // end RaceCondition outter class