2015-03-03 11 views
5

C'è qualche differenza tra AtomicReference e Synchronized?
E.G.Qual è la differenza tra AtomicReference e Synchronized?

public class Internet { 
    AtomicReference<String> address; 
    public String getAddress(){ 
     return address.toString(); 
    } 
    public void setAddress(String address) { 
     this.address.set(address); 
    } 

} 

e passo alla classe di alcuni fili che cercano di utilizzare la classe allo stesso tempo, è la stessa cosa se uso questo:

public class Internet { 
    String address; 
    public String getAddress(){ 
     return address; 
    } 
    public void setAddress(String address) { 
     this.address = address; 
    } 
} 

E poi nell'uso filo synchronized prima dell'accesso alla classe?

+2

È funzionalmente lo stesso, se è quello che stai chiedendo. Il modello di concorrenza è diverso. –

+0

Vedere http://docs.oracle.com/javase/tutorial/essential/concurrency/atomicvars.html "Per questa classe semplice, la sincronizzazione è una soluzione accettabile, ma per una classe più complicata, potremmo voler evitare l'impatto sulla durata di sincronizzazione non necessaria. " Come sopra, funzionalmente sono uguali. – jdv

risposta

7

Lei non è stato inizializzato il riferimento nel primo esempio, probabilmente dovrebbe essere:

public class Internet { 
    AtomicReference<String> address = new AtomicReference<String>(); 
    public String getAddress(){ 
     String s = address.get(); 
     return s == null ? null : s.toString(); 
    } 
    public void setAddress(String address) { 
     this.address.set(address); 
    } 
} 

Dove si trova l'restrizione di accesso è importante. Se metti il ​​controllo all'interno dell'oggetto a cui si accede, allora può avere il controllo esclusivo dei suoi invarianti, che è molto meno fragile che fare affidamento sui thread per sincronizzarli tutti correttamente, dove un thread di accesso mal funzionante può corrompere la cosa a cui si accede. Quindi il primo esempio è molto meglio su quell'account.

Se si modifica il secondo esempio in modo che l'oggetto ha il controllo su un proprio blocco (in modo che non si basa su discussioni accedervi a farlo in modo sicuro), in questo modo:

public class Internet { 
    private final Object lock = new Object(); 
    private String s; 
    public String getAddress() { 
     synchronized(lock) { 
      return s; 
     } 
    } 
    public void setAddress(String s) { 
     synchronized(lock) { 
      this.s = s; 
     } 
    } 
} 

allora è una più stretta confronto, uno si basa sul blocco e l'altro sui riferimenti atomici. Quello che usa AtomicReference cerca di evitare il blocco usando istruzioni di elaborazione atomica a livello di macchina. Quale è più veloce può dipendere dal tuo hardware e jvm e il carico di elaborazione, in genere l'approccio atomico dovrebbe essere più veloce. L'approccio sincronizzato è un meccanismo più generale; con il blocco sincronizzato è possibile raggruppare più assegnazioni molto più facilmente, dove con riferimenti atomici è molto più coinvolto.

As James says in his answer, con la sincronizzazione i thread sono in attesa di un blocco; non c'è il timeout e il deadlock è possibile. Con il riferimento atomico, il thread esegue la modifica senza attendere un blocco condiviso.

Il modo più semplice e migliore performance per implementare questo potrebbe essere quella di organizzare il codice in modo da poter rendere l'oggetto immutabile, quindi si dovrebbe evitare ogni blocco, occupato-attesa, e l'aggiornamento della cache:

public final class Internet { 
    private final String s; 
    public Internet(String s) { 
     this.s = s; 
    } 
    public String getAddress() {return s;} 
} 

In ordine decrescente di preferenza:

  • Preferire l'immutabilità quando possibile.
  • Per il codice che non può essere immutabile, provare a limitare la mutazione a un thread.
  • Se solo una cosa deve passare attraverso i thread, utilizzare l'approccio atomico.
  • Se più modifiche ai thread devono verificarsi insieme indisturbate da altri thread, utilizzare il blocco.
2

A synchronized metodo/blocchi di blocco tutti gli accessi a tale metodo/blocco da altri thread mentre un thread sta eseguendo il metodo.

A Atomic... È possibile accedere a Atomic... da più thread contemporaneamente: di solito sono disponibili i metodi di accesso CAS per consentire l'accesso ad alta velocità.

Come tali, sono completamente diversi ma a volte possono essere utilizzati per risolvere problemi di accessibilità paralleli.

Queste due classi utilizzano i due diversi metodi per restituire un numero sempre crescente tale che lo stesso numero non verrà mai consegnato due volte. La versione AtomicInteger verrà eseguita più rapidamente in un ambiente ad alto carico. Quella che utilizza synchronized funzionerà in Java 4 e versioni precedenti.

public class Incremental1 { 

    private AtomicInteger n = new AtomicInteger(); 

    public Integer nextNumber() { 
     // Use the Atomic CAS function to increment the number. 
     return n.getAndIncrement(); 
    } 
} 

public class Incremental2 { 

    private int n = 0; 

    public synchronized Integer nextNumber() { 
     // No two threads can get in here at the same time. 
     return n++; 
    } 

} 
+1

Ho pensato di leggere in JCIP che in genere l'atomica era preferibile per contesa da bassa a media, non per carichi elevati così tanto? –

+0

@NathanHughes - Dipende da cosa intendi per carico elevato. Atomics funziona bene sotto tutto ma il carico eccezionalmente alto. Usando l'atomica è spesso necessario eseguire lo spinlock sull'atomico e con carichi molto elevati ciò può causare un enorme degrado, ma in tali condizioni 'synchronized' sarebbe caduto in una scansione molto tempo fa. – OldCurmudgeon

+0

@OldCurmudgeon L'ultima istruzione non è completamente vera. In caso di contesa più alta un "BlockingQueue" ha prestazioni migliori di un "ConcurrentLinkedQueue". Non ci saranno errori, ma solo la programmazione dei thread. Quindi direi che la dichiarazione di Nathan è corretta. –

3

Non c'è niente di sbagliato con le altre risposte qui se li si può capire, ma la maggior parte essi sembrano concentrarsi sui dettagli, nomenclatura, e casi d'uso, mentre saltando il quadro generale che "tutti" già conosce.

Ecco l'immagine grande --- la differenza tra un'operazione AtomicFoobar e un blocco synchronized.

Un'operazione AtomicFoobar (ad es. AtomicReference.compareAndSet (...)) esegue esattamente una, molto semplice operazione thread-safe, oppure non riesce. Indipendentemente dal fatto che abbia successo o fallisca, non farà mai aspettare il thread.

Un blocco synchronized, al contrario è complicato come lo si fa --- non c'è limite a quante istruzioni vengono eseguite mentre il blocco è bloccato. Un blocco synchronized sarà mai non riuscito, ma è può rendere il thread chiamante attendere finché le operazioni possono essere eseguite in modo sicuro.

Sulla maggior parte delle architetture, ciascun metodo AtomicFoobar è implementato come metodo Java nativo (ad esempio codice C) che esegue una singola istruzione hardware specializzata. Sincronizzato, d'altra parte è sempre più implementato con chiamate del sistema operativo che, da qualche parte nel profondo, probabilmente utilizzano le stesse istruzioni hardware.

Problemi correlati