2013-02-25 11 views
12
class MyClass 
{ 
     private static volatile Resource resource; 

     public static Resource getInstance() 
     { 
      if(resource == null) 
        resource = new Resource(); 
      return resource; 
     } 
} 

Ecco il mio dubbio è secondo a Java concorrenza in pratica se si utilizza volatile, la pubblicazione sicura accade (vale a dire non appena il riferimento è visibile a un altro thread i dati sono anche disponibili). Quindi posso usarlo qui? Ma se è corretto, supponiamo che thread1 ora controlli "resource" ed è null, quindi inizia a creare l'oggetto. Mentre thread1 sta creando l'oggetto di un altro thread, ad esempio thread2, viene e inizia a controllare il valore di "resource" e thread2 lo trova come null (supponiamo che la creazione dell'oggetto "resource" richieda una notevole quantità di tempo e poiché thread1 non ha ancora completato la creazione la pubblicazione sicura non è avvenuta quindi non disponibile per thread2) quindi inizierà anche a creare l'oggetto? se sì, allora le rotture invarianti di classe. Ho ragione? Per favore aiutami a capire questo uso speciale di volatile qui.Singleton con volatili in Java

+2

Non si utilizzerà 'volatile' in un singleton. * Per definizione * l'istanza privata all'interno del singleton non cambierà il che significa che non vi è alcun pericolo che un thread memorizzi un vecchio valore nella cache. Si sta ignorando che al momento hai un'implementazione non a prova di codice perché il tuo 'getInstance' non è sincronizzato. Usa un 'enum' per creare singleton in Java. –

+0

http://jeremymanson.blogspot.com/2008/11/what-volatile-means-in-java.html – happybuddha

+1

Per quasi tutti gli usi si potrebbe anche usare l'espressione di inizializzazione mentre il caricamento delle classi viene eseguito pigramente. Se stai usando la classe per qualche altra ragione, forse senza usare questa istanza (che suona male), allora una classe annidata per contenere la statica farà bene il lavoro. –

risposta

11

volatile risolve un problema che è problema di visibilità. Se si sta scrivendo a una variabile dichiarata volatile, il valore sarà immediatamente visibile all'altro thread. Come tutti sappiamo abbiamo un diverso livello di cache in OS L1, L2, L3 e se scriviamo su una variabile in un thread non è garantito che sia visibile ad altri, quindi se usiamo volatile scrive per dirigere la memoria ed è visibile per gli altri. Ma volatile non risolve il problema dell'atomicità , ovvero int a; a++; non è sicuro. COME ci sono tre istruzioni macchina associate ad esso.

15

Si è corretto, più thread potrebbero tentare di creare un oggetto risorsa. Volatile garantisce solo che se un thread aggiorna il riferimento, tutti gli altri thread vedranno il nuovo riferimento, non un riferimento memorizzato nella cache. Questo è più lento, ma più sicuro.

Se avete bisogno di una sola risorsa che è caricato in modo pigro, è necessario fare qualcosa di simile:

class MyClass 
{ 
     private static volatile Resource resource; 
     private static final Object LOCK = new Object(); 

     public static Resource getInstance() 
     { 
      if(resource == null) { 
       synchronized(LOCK) { // Add a synch block 
        if(resource == null) { // verify some other synch block didn't 
              // write a resource yet... 
         resource = new Resource(); 
        } 
       } 
      } 
      return resource; 
     } 
} 
+0

Preferisco usare gli oggetti di blocco http://docs.oracle.com/javase/ tutorial/essential/concorrenza/newlocks.html – ssedano

+1

@corsiKa Grazie per la pronta risposta. Posso definirlo in questo modo "Poiché volatile risolve solo il problema di visibilità conformandosi all'idioma prima, ma qui in questo caso abbiamo bisogno di visibilità oltre che di atomicità, cioè controllo della condizione e inizializzazione dell'oggetto (risorsa), quindi inizializzatore sincronizzato o statico è obbligatorio". Perfavore, correggimi se sbaglio. – Trying

+0

@ user2109070 Direi che è un riepilogo sufficiente. Se stavo facendo una revisione del codice sul codice, sarebbe una dichiarazione ragionevole da fare. – corsiKa

0

volatile garanzie di parola chiave che leggono e scrivono a quella variabile sono atomiche.

Secondo la tutorial

Reads and writes are atomic for all variables declared volatile 

Utilizzo di variabili volatili riduce il rischio di coerenza memoria errori, perché ogni scrittura a una variabile volatile, stabilisce una accade-prima relazione con conseguente letture dello stesso variabile. Ciò significa che le modifiche a una variabile volatile sono sempre visibili ad altri thread. Inoltre, significa che quando un thread legge una variabile volatile, non vede solo l'ultima modifica volatile, ma anche gli effetti collaterali del codice che ha portato la modifica a .

0

Quando applicato ad un campo, le garanzie volatili Java che:

  1. (In tutte le versioni di Java) C'è un ordine globale sulle letture e scrive su una variabile volatile. Ciò implica che ogni thread che accede a un campo volatile leggerà il suo valore corrente prima di continuare anziché (potenzialmente) utilizzando un valore memorizzato nella cache. (Tuttavia, non c'è alcuna garanzia sull'ordinamento relativo delle letture volatili e scrive con letture e scritture regolari, il che significa che è in genere non un utile costrutto di threading.)

  2. (In Java 5 o successivo) Volatile legge e scrive stabilire un rapporto accade-prima, proprio come l'acquisizione e rilasciando un mutex .

Più info.

+2

Ma non hai risposto alla mia domanda. :) – Trying

7

So che non stai chiedendo soluzioni migliori, ma questo è sicuramente la pena se stai cercando una soluzione pigro singleton.

Utilizzare una classe statica privata per caricare il singleton. La classe non viene caricata fino al richiamo e quindi il riferimento non viene caricato fino alla classe. Il caricamento delle classi per implementazione è thread-safe e si verificano anche pochissime spese generali (nel caso si stiano facendo carichi volatili ripetitivi [che possono essere ancora a buon mercato], questa risoluzione carichi sempre normali dopo la costruzione iniziale).

class MyClass { 
    public static Resource getInstance() { 
     return ResourceLoader.RESOURCE; 
    } 

    private static final class ResourceLoader { 
     private static final Resource RESOURCE = new Resource(); 
    } 
} 
0

prima di tutto, avere un Singleton in questo modo, si sono essenzialmente la creazione di un oggetto globale che è una cattiva pratica. Suppongo che tu usi invece Enums.

+3

Ma anche gli Enum sono globali. –

0

Ecco il mio suggerimento di aggiungere volatile & sincronizzato insieme.

Nota: dobbiamo ancora fare doppio controllo.

public class MySingleton { 
    private static volatile MySingleton instance; 
    private MySingleton() {} 

    synchronized private static void newInstance() { 
     if(instance == null) { 
      instance = new MySingleton(); 
     } 
    } 

    public static MySingleton get() { 
     if(instance == null) { 
      newInstance(); 
     } 
     return instance; 
    } 
}