java concurrency this publishing

java - ¿Qué es un "objeto construido de forma incompleta"?



concurrency this (3)

La concurrencia de Java de Goetz en la práctica , página 41, menciona cómo this referencia puede escapar durante la construcción. Un ejemplo de "no hagas esto":

public class ThisEscape { public ThisEscape(EventSource source) { source.registerListener( new EventListener() { public void onEvent(Event e) { doSomething(e); } }); } }

Aquí se trata de "escapar" a través del hecho de que doSomething(e) refiere a la instancia ThisEscape . La situación se puede arreglar usando métodos de fábrica estáticos (primero construya el objeto plano, luego registre al oyente) en lugar de constructores públicos (haciendo todo el trabajo). El libro continúa:

Publicar un objeto desde su constructor puede publicar un objeto construido de forma incompleta. Esto es cierto incluso si la publicación es la última declaración en el constructor. Si this referencia se escapa durante la construcción, el objeto se considera no construido correctamente.

No entiendo muy bien esto. Si la publicación es la última declaración en el constructor, ¿no se ha hecho todo el trabajo de construcción antes de eso? ¿Cómo es que this no es válido para entonces? Aparentemente hay algo de vudú después de eso, pero ¿qué?


El final de un constructor es un lugar especial en términos de concurrencia, con respecto a los campos finales. De la sección 17.5 de la especificación del lenguaje Java:

Se considera que un objeto está completamente inicializado cuando finaliza su constructor. Se garantiza que un hilo que solo puede ver una referencia a un objeto después de que ese objeto se haya inicializado completamente ve los valores correctamente inicializados para los campos finales de ese objeto.

El modelo de uso para los campos finales es simple. Establezca los campos finales para un objeto en el constructor de ese objeto. No escriba una referencia al objeto que se está construyendo en un lugar donde otro hilo pueda verlo antes de que el constructor del objeto finalice. Si se sigue esto, entonces, cuando el objeto es visto por otro hilo, ese hilo siempre verá la versión correctamente construida de los campos finales de ese objeto. También verá versiones de cualquier objeto o matriz a la que hagan referencia aquellos campos finales que estén al menos tan actualizados como los campos finales.

En otras palabras, su oyente podría terminar viendo los campos finales con sus valores predeterminados si examina el objeto en otro hilo. Esto no sucedería si el registro del oyente ocurriera después de que el constructor haya completado.

En cuanto a lo que está sucediendo, sospecho que hay una barrera de memoria implícita al final de un constructor, asegurándose de que todos los subprocesos "vean" los nuevos datos; sin que se haya aplicado esa barrera de memoria, podría haber problemas.


Hay un tiempo pequeño pero finito entre el final del registroListener y el regreso del constructor. Otro subproceso que podría utilizar entró en ese momento e intenta llamar a doSomething (). Si el tiempo de ejecución no regresó directamente a su código en ese momento, el objeto podría estar en un estado no válido.

Realmente no estoy seguro de Java pero un ejemplo que se me ocurre es donde posiblemente el tiempo de ejecución reubica la instancia antes de volver a ti.

Es una pequeña posibilidad que te concedo.


Otro problema surge cuando se crea una subclase de ThisEscape, y la clase secundaria invoca a este constructor. Lo implícito de esta referencia en el EventListener tendría un objeto construido de forma incompleta.