he estado investigando acerca de la doble comprobado bloqueo lenguaje y de lo que he entendido, el código podría dar lugar al problema de la lectura de una instancia parcialmente construido a menos que su clase de prueba es inmutable:
El Java Memory Model ofrece una garantía especial de seguridad de inicialización para compartir objetos inmutables.
Se puede acceder de forma segura incluso cuando la sincronización no se utiliza para publicar la referencia del objeto.
(Las citas del libro muy recomendable Java concurrencia en la práctica)
Así que en ese caso, el doble bloqueo comprueban idioma funcionaría.
Pero, si ese no es el caso, observe que está devolviendo la instancia variable sin sincronización, por lo que la variable de instancia puede no estar completamente construida (vería los valores predeterminados de los atributos en lugar de los valores provistos en constructor).
La variable booleana no aporta nada para evitar el problema, ya que puede establecerse en true antes de que se inicializa la clase de prueba (la palabra clave sincronizada no evita por completo la reordenación, algunos sencences pueden cambiar el orden). No hay regla de sucede antes en el Modelo de memoria de Java para garantizar eso.
Y hacer que el booleano sea volátil tampoco agregaría nada, porque las variables de 32 bits se crean atómicamente en Java. La expresión idiomática de bloqueo doble también funcionaría con ellos.
Desde Java 5, puede solucionar ese problema declarando la variable de instancia como volátil.
Puede leer más sobre la expresión doble marcada en this very interesting article.
Por último, algunas recomendaciones que he leído:
considerar si se debe utilizar el patrón singleton. Muchas personas lo consideran un antipatrón. Se prefiere la inyección de dependencia siempre que sea posible. Compruebe this.
Considere cuidadosamente si la optimización de doble bloqueo comprobado es realmente necesaria antes de implementarla, porque en la mayoría de los casos, eso no valdría la pena el esfuerzo. Además, considere la posibilidad de construir la clase de prueba en el campo estático, porque la carga diferida solo es útil cuando la construcción de una clase requiere una gran cantidad de recursos y en la mayoría de los casos, no es el caso.
Si todavía es necesario realizar esta optimización, marque esta link que proporciona algunas alternativas para lograr un efecto similar a lo que está tratando.
Lo uso. La razón por la que funciona es porque la instancia se instanciará por completo antes de que se toque la línea initialized = true y la asignación de un valor booleano en Java es una operación atómica (en cada chipset que he usado). – Jono
@Jono Depende del compilador y la VM, si funciona o no. El compilador puede producir un bytecode que está cacarando el valor de la expresión '! Initialized', y por lo tanto falta si otro hilo cambia el valor de la propiedad' initialized'. Si el mismo código se ejecuta de forma paralela, la inicialización de la instancia se puede ejecutar dos veces. Además, el compilador JIT puede optimizar esta expresión a partir de un bytecode no optimizado. Solo piense: la VM debe evaluar la misma expresión, inmediatamente después de la otra. Una operación 'dup' impide la reevaluación, y es una transformación de código válida según JLS. – gaborsch