Non credo che nessuno abbia effettivamente risposto alla domanda , quindi proverò.
Il volatile e il primo if (instance == null)
non sono "necessari". Il blocco renderà questo codice thread-safe.
Quindi la domanda è: perché dovresti aggiungere il primo if (instance == null)
?
Il motivo è presumibilmente quello di evitare di eseguire inutilmente la sezione di codice bloccata. Mentre si esegue il codice all'interno del blocco, qualsiasi altro thread che tenta di eseguire anche quel codice viene bloccato, il che rallenterà il programma se si tenta di accedere frequentemente al singleton da molti thread. A seconda della lingua / piattaforma, potrebbero esserci anche dei sovraccarichi dalla serratura stessa che si desidera evitare.
Quindi il primo controllo nullo viene aggiunto come un modo molto rapido per vedere se è necessario il blocco. Se non è necessario creare il singleton, è possibile evitare completamente il blocco.
Ma non puoi controllare se il riferimento è nullo senza bloccarlo in qualche modo, perché a causa del caching del processore, un altro thread potrebbe cambiarlo e tu leggeresti un valore "non aggiornato" che ti porterebbe a inserire il blocco inutilmente. Ma stai cercando di evitare una serratura!
Quindi rendi il singleton volatile per assicurarti di leggere il valore più recente, senza la necessità di utilizzare un blocco.
Hai ancora bisogno del lucchetto interno perché volatile ti protegge solo durante un singolo accesso alla variabile: non puoi testarlo e impostarlo in modo sicuro senza utilizzare un lucchetto.
Ora, è davvero utile?
Beh, direi "nella maggior parte dei casi, no".
Se Singleton.Instance potesse causare inefficienza a causa dei blocchi, allora perché lo chiami così spesso che questo sarebbe un problema significativo ? Il punto centrale di un singleton è che ce n'è solo uno, quindi il tuo codice può leggere e memorizzare nella cache il riferimento singleton una volta.
L'unico caso in cui riesco a pensare a dove questa memorizzazione nella cache non sarebbe possibile sarebbe quando si dispone di un numero elevato di thread (ad esempio un server che utilizza un nuovo thread per elaborare ogni richiesta potrebbe creare milioni di thread a esecuzione molto breve, ciascuno dei che dovrebbe chiamare Singleton.Instance una volta).
Quindi sospetto che il doppio controllo del blocco sia un meccanismo che ha un posto reale in casi molto specifici critici per le prestazioni, e quindi tutti si sono arrampicati sul carro del "questo è il modo corretto di farlo" senza realmente pensare a cosa fa e se sarà effettivamente necessario nel caso in cui lo stiano usando.