2012-01-14 7 views
12

La lectura de este sitio, me he encontrado this:¿Por qué esta variable estática final en un singleton thread-safe?

[El] línea de private static final Foo INSTANCE = new Foo(); sólo se ejecuta cuando se utiliza realmente la clase, este se encarga de la creación de instancias perezoso, y es que garantiza que sea seguro para subprocesos.

¿Por qué esto garantiza ser seguro para subprocesos? Debido a que este campo es final? ¿O por alguna otra razón?

Respuesta

21

Porque es definitiva, sí. Las variables finales tienen una semántica especial de seguridad de subprocesos, ya que se garantiza que otros subprocesos verán el campo final al menos en el estado en que se encontraba cuando finalizó su constructor.

Esto está en JLS 17.5, aunque el idioma es un poco denso. Esta semántica se introdujo en Java 1.5, en particular por JSR-133. Consulte esta página para obtener una discusión sin especificaciones de JSR-133 y sus diversas implicaciones.

Tenga en cuenta que si modifica la instancia después de su constructor, es decir no necesariamente seguro para hilos. En ese caso, debe tomar las precauciones de seguridad habituales del hilo para asegurarse de que suceda antes de los bordes.

Estoy bastante seguro (aunque no del todo 100%) de que el hecho de que solo un hilo haga la inicialización de clase es no un factor aquí. Es cierto que la clase solo se inicia con un subproceso, pero no creo que haya ningún caso específico: antes de que se establezcan los límites entre ese subproceso y cualquier otro subproceso que use la clase (que no sea el otro subproceso que no tenga que reiniciarse) la clase). Por lo tanto, sin la palabra clave final, otro subproceso podría ver una instancia parcialmente construida del objeto. Los bordes específicos de happen-before que JMM define están en JLS 17.4.5, y la inicialización de clase no se encuentra allí.

+0

sí, gracias. Gran respuesta – MyTitle

+0

Creo que la razón más importante que la definitiva es que la inicialización de la clase (inicialización estática) debe estar sincronizada y protegida contra subprocesos. Usar la sincronización establece el hecho antes de ordenar. Consulte 12.4.2 (http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.4.2) para obtener más detalles. – sjlee

+0

@sjlee Es cierto que la inicialización requiere bloqueos, pero si el hilo A inicializa MyClass, y el hilo B aparece unos 5 minutos más tarde y utiliza MyClass, el hilo B no intentará inicializar MyClass (porque ya se ha inicializado). Eso significa que no obtendrá el bloqueo en 'MyClass.class', lo que significa que no hay relación de ocurrencia previa entre las acciones del subproceso A y el subproceso B. Si cada hilo se bloqueara en 'MyThread.class' cada vez que lo usara, nunca necesitaría establecer ninguna otra relación de sucede antes. – yshavit

2

Se garantiza que el bloque de inicialización estática de cualquier clase sea de un solo hilo. Un singleton más simple es usar una enumeración

enum Singleton { 
    INSTANCE; 
} 

Esto también es seguro para hilos y la clase iniciada en modo perezoso.

+0

Referencia para esto, p. JLS? – paislee

+0

@paislee: me siento más a gusto en el JVMS, así que puedo proporcionarle una [referencia a eso] (http://java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc. html # 24237). Si sigue el procedimiento, verá que la inicialización de la clase solo puede realizarse con un único hilo (por cargador de clases). El inicializador estático se llama en el paso 8. – musiKk

+1

El JVMS proporciona una semántica más estricta que el JLS de varias maneras, por lo que no es una gran fuente para la corrección general del idioma. El JLS define la inicialización de clases en [JLS 12.4] (http://java.sun.com/docs/books/jls/third_edition/html/execution.html#12.4), y 12.4.2 detalla el procedimiento específicamente. – yshavit

3

Se garantiza que es seguro para la ejecución de subprocesos porque la JVM garantiza que los inicializadores estáticos se ejecuten en un solo subproceso.

No significa que la instancia de Foo sea internamente segura para los hilos, solo significa que se le garantiza que el constructor de Foo se llamará exactamente una vez, en un hilo, a través de esta ruta de código particular.

5

constructores de clase e inicializadores estática/instancia están garantizados para ser ejecutado atómicamente y desde private static final FOO INSTANCE = new FOO; es equivalente a

private static final FOO INSTANCE; 

static{ 
    INSTANCE = new FOO(); 
} 

este caso cae en la categoría arriba.

Cuestiones relacionadas