Consideriamo la doppia classic checked esempio blocco di capire perché un riferimento deve essere atomica:
class Foo {
private Helper result;
public static Helper getHelper() {
if (result == null) {//1
synchronized(Foo.class) {//2
if (result == null) {//3
result = new Helper();//4
}
}
}
return result//5;
}
// other functions and members...
}
Consideriamo 2 discussioni che stanno andando a chiamare il getHelper
metodo:
- Thread-1 esegue il numero di riga 1 e trova
result
per essere null
.
- Thread-1 acquisisce un blocco livello di classe nella riga numero 2
- Thread-1 trova
result
essere null
nella riga numero 3
- Thread-1 avvia un'istanza di un nuovo
Helper
- Mentre Thread-1 è ancora istanziando un nuovo
Helper
sulla riga numero 4, Thread-2 esegue il numero di riga 1.
I passaggi 4 e 5 sono i punti in cui può verificarsi un'incoerenza. È possibile che al punto 4 l'oggetto non sia completamente istanziato ma che la variabile result
abbia già già inserito l'indirizzo dell'oggetto Helper
parzialmente creato. Se lo Step-5 esegue anche un nanosecondo prima che l'oggetto Helper
sia completamente inizializzato, Thread-2 vedrà che il riferimento result
non è null
e potrebbe restituire un riferimento a un oggetto parzialmente creato.
Un modo per risolvere il problema è contrassegnare result
come volatile
o utilizzare AtomicReference
.Detto questo, lo scenario sopra descritto è altamente improbabile che si verifichi nel mondo reale e ci sono modi migliori per implementare uno Singleton
rispetto all'utilizzo del blocco a doppio controllo.
Here's un esempio di attuazione di interblocco ricontrollato con AtomicReference
:
private static AtomicReference instance = new AtomicReference();
public static AtomicReferenceSingleton getDefault() {
AtomicReferenceSingleton ars = instance.get();
if (ars == null) {
instance.compareAndSet(null,new AtomicReferenceSingleton());
ars = instance.get();
}
return ars;
}
Se siete interessati a sapere perché Fase 5 può provocare incongruenze di memoria, dare un'occhiata a this risposta (come suggerito da pwes in i commenti)
* riferimento parzialmente costruito *. Trovo che questa linea sia un po 'errata. – CKing
@ChetanKinger: è meglio ora? –
Quello che stavo cercando di indicare è che il motivo per cui abbiamo bisogno di un riferimento atomico è evitare che i thread utilizzino un riferimento a un oggetto parzialmente costruito.Invece di * riferimento parzialmente costruito *, la tua risposta dovrebbe leggere * il riferimento a un oggetto parzialmente costruito *. (Secondo me) – CKing