[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Lazy initialization/Singletons
- To: mindfood@xxxxxxxxxxxxxxx
- Subject: Lazy initialization/Singletons
- From: Jose San Leandro <jose.sanleandro@xxxxxxxxxxxx>
- Date: Sun, 14 May 2006 01:47:57 +0200
- Delivered-to: mailing list mindfood@orange-soft.com
- Delivered-to: moderator for mindfood@orange-soft.com
- Mailing-list: contact mindfood-help@orange-soft.com; run by ezmlm
- Organization: Ventura24
- User-agent: KMail/1.9.1
Acabo de releer sobre Java Memory Model [1] , y sobre el Double-Checked
Locking [2], y voy a tener que asumir que el mecanismo de inicialización
tardía (Lazy initalization), usado frecuentemente sobre todo en el patrón
Singleton, no respeta el modelo. Es válido algorítmicamente, pero no es
compatible con la especificación del lenguaje.
Para entender los problemas derivados del JMM, hay que centrarse
fundamentalmente en:
1) Crear una instancia mediante "a = new A();" es en realidad una secuencia de
acciones:
1.1) reservar memoria en el Heap.
1.2) invocar el constructor.
1.3) asignar a la variable local "a" la dirección de memoria creada.
2) El JMM da libertad a los compiladores y máquinas virtuales a la hora de
realizar optimizaciones:
2.1) guardar datos en registros o cachés y refrescar el contenido de la
memoria posteriormente,
2.2) cambiar el orden en el que se ejecutan las instrucciones, siempre que el
resultado final sea idéntico al que daría lugar una ejecución secuencial.
Es necesario tener en cuenta ésto para entender los problemas de concurrencia
que se pueden dar, y que derivan en una cierta incertidumbre sobre lo que en
realidad dicta el código que escribimos en entornos multi-hilo o
concurrentes. De hecho, lo que prima es descartar la implementación buscando
un contraejemplo [3], teniendo en cuenta que si ya es no trivial pensar en
hebras cooperando para molestarse entre sí, más aún lo es si hay que suponer
que un "new" seguido de una comparación con "null" puede dar lugar a que la
lectura se dé antes que la escritura, y que lo que vea la hebra que hace la
comparación sea un objeto creado, pero sin inicializar. A este respecto,
conviene resaltar justamente que la palabra reservada "volatile" en Java
trata precisamente de eso: de impedir que el compilador y/o JVM cambie de
orden las lecturas y las escrituras del atributo dado.
Resumiendo, parece que hay dos alternativas:
1) Crear una clase aparte y declarar el singleton como una variable estática
de la misma.
2) Usando instancias ThreadLocal indicando a cada hebra si ha realizado la
pertinente sincronización o no [3]
Hay un benchmark [4] para ayudarnos a decidir :).
Por simplicidad, me inclino por la primera opción, aunque aprovecho para
añadir una crítica a XP: ¿Cómo se supone que se puede hacer el código
autoexplicativo en este caso, de forma que no sea necesario incluir
comentarios que aclaren su propósito?
Un saludo,
Jose.
[1] http://www.cs.umd.edu/~pugh/java/memoryModel/
[2] http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl
[3]
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html#ThreadLocal
[4] http://www.cs.umd.edu/~pugh/java/memoryModel/DCL-performance.html