No creo que nadie haya respondido realmente a la pregunta , así que lo intentaré.
Los volátiles y los primeros if (instance == null)
no son "necesarios". El bloqueo hará que este código sea seguro para subprocesos.
Entonces la pregunta es: ¿por qué agregarías el primero if (instance == null)
?
La razón es presumiblemente para evitar ejecutar la sección bloqueada de código innecesariamente. Mientras está ejecutando el código dentro del bloqueo, cualquier otro hilo que intente ejecutar también ese código se bloquea, lo que ralentizará su programa si intenta acceder al singleton con frecuencia desde muchos hilos. Dependiendo del idioma / plataforma, también puede haber gastos generales de la cerradura que desea evitar.
Entonces, la primera verificación nula se agrega como una forma realmente rápida de ver si necesita el bloqueo. Si no necesita crear el singleton, puede evitar el bloqueo por completo.
Pero no puede verificar si la referencia es nula sin bloquearla de alguna manera, porque debido al almacenamiento en caché del procesador, otro hilo podría cambiarla y leería un valor "obsoleto" que lo llevaría a ingresar el bloqueo innecesariamente. ¡Pero estás intentando evitar un candado!
Por lo tanto, hace que el singleton sea volátil para asegurarse de leer el valor más reciente, sin necesidad de usar un candado.
Aún necesita el candado interno porque volátil solo lo protege durante un acceso único a la variable; no puede probarlo y configurarlo de manera segura sin usar un candado.
Ahora bien, ¿es esto realmente útil?
Bueno, yo diría "en la mayoría de los casos, no".
Si Singleton.Instance podría causar ineficiencia debido a las cerraduras, entonces ¿por qué lo llama con tanta frecuencia que esto sería un problema importante ? El objetivo de un singleton es que solo hay uno, por lo que su código puede leer y almacenar en caché la referencia singleton una vez.
El único caso en el que puedo pensar en el que este almacenamiento en caché no sería posible sería cuando tiene una gran cantidad de subprocesos (por ejemplo, un servidor que usa un nuevo subproceso para procesar cada solicitud podría estar creando millones de subprocesos de ejecución muy corta, cada uno de los cuales que tendría que llamar a Singleton.Instance una vez).
Así que sospecho que el bloqueo con doble verificación es un mecanismo que tiene un lugar real en casos muy específicos de rendimiento crítico, y luego todos se han subido al tren de "esta es la forma correcta de hacerlo" sin pensar realmente qué hace y si será realmente necesario en el caso de que lo estén usando.