Pregunta

En referencia a mi pregunta anterior en forma incompleta construida objetos , tengo una segunda pregunta. Como Jon Skeet señaló, hay una barrera de memoria implícita en el extremo de un constructor que se asegura de que los campos final son visibles para todos los temas. Pero lo que si un constructor llama a otro constructor; ¿hay una barrera de este tipo de memoria en el final de cada uno de ellos, o sólo en el extremo del mismo que se ha llamado en primer lugar? Es decir, cuando la solución "mal" es:

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

Y el correcto sería una versión método de fábrica:

public class SafeListener {
    private final EventListener listener;

    private SafeListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        }
    }

    public static SafeListener newInstance(EventSource source) {
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }
}

¿Podría el siguiente trabajo también, o no?

public class MyListener {
    private final EventListener listener;

    private MyListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        }
    }

    public MyListener(EventSource source) {
        this();
        source.register(listener);
    }
}

Actualización: La cuestión esencial es que es this() garantizado que en realidad llamada el constructor privado anteriormente (en cuyo caso no habría la barrera cuando se destinen y todo estaría a salvo ), o es posible que el constructor privado obtiene inline en la pública como una optimización para salvar una barrera de memoria (en cuyo caso no habría una barrera hasta que al final del constructor público )?

son las reglas de this() definido con precisión en alguna parte? Si no es así, entonces creo que debemos asumir que inlining constructores encadenados está permitido, y probablemente algunas JVM o tal vez incluso javacs lo están haciendo.

¿Fue útil?

Solución

Creo que es seguro como estados modelo de memoria de Java que:

  

Let o ser un objeto, y c ser un constructor de o en el que una final   campo f se guarda. Una acción de congelación de campo final f de o se lleva a cabo   cuando c salidas, ya sea normal o abruptamente. Tenga en cuenta que si uno   constructor invoca a otro constructor, y el constructor invoca   establece un campo final, la congelación para el campo final se lleva a cabo en el   terminar del constructor invocado.

Otros consejos

  se considera

Un objeto a ser completamente inicializado cuando sus acabados constructor.

Esto se aplica también para los constructores encadenados.

Si usted tiene que registrarse en el constructor definir el oyente como una clase interna estática. Esto es seguro.

Su segunda versión no es correcta, ya que está permitiendo que el 'esto' referencia a escapar del proceso de construcción. Tener 'esta' fuga invalida las garantías de seguridad de inicialización que dan campos finales de su seguridad.

Para abordar la cuestión implícita, la barrera en el extremo de la construcción sólo ocurre al final de la construcción objeto. El lector intuición ofrecida sobre procesos en línea es un útil; desde la perspectiva del modelo de memoria de Java, no existen límites del método.

Editar Después del comentario que sugería que el compilador inlining el constructor privado (no lo había pensado de esa optimización) lo más probable es que el código será inseguro. Y la peor parte del código multiproceso inseguro es que es parece que funciona, por lo que es mejor evitar por completo. Si quieres jugar a diferentes trucos (usted realmente quiere evitar la fábrica por alguna razón) considerar la adición de un envoltorio para garantizar la coherencia de los datos en el objeto de aplicación interna y registrar en el objeto externo.


Mi suposición es que va a ser frágil, pero está bien. El compilador no puede saber si el constructor interna se llamará sólo desde dentro de otros constructores o no, por lo que tiene que asegurarse de que el resultado sería correcto para el código de llamada sólo el constructor interna, por lo que cualquier mecanismo que utiliza (barrera de memoria?) Tiene a estar en su lugar allí.

Me imagino que el compilador añadiría la barrera de memoria al final de cada constructor. El problema sigue ahí: usted está pasando la referencia this a otro código (posiblemente otros hilos) antes de que esté totalmente construido --que es bad--, pero si la única'construction' que queda es el registro del oyente, entonces el el estado del objeto es tan estable como lo será nunca.

La solución es frágil en la que algún otro día, o algún otro programador puede que tenga que añadir otro miembro al objeto y puede olvidar que los constructores encadenadas es un truco de concurrencia y puede decidir para inicializar el campo en el constructor público, y al hacerlo, se sumará un disco para detectar el potencial carrera de datos en la aplicación, por lo que me gustaría tratar de evitar esa construcción.

Por cierto: La seguridad puede ser adivinado mal. No sé lo complejo / Smart es el compilador, y si la barrera de memoria (o similar) es algo que podría tratar de optimizar distancia ... ya que el constructor es privado el compilador tiene información suficiente para saber que es Sólo llamados de otros constructores, y que es suficiente información para determinar que el mecanismo de sincronización no es necesaria en el constructor interna ...

Escapar referencia de objeto en c-tor puede publicar un objeto de forma incompleta construido. Esto es cierto incluso si la publicación es la última instrucción en el constructor .

Su SafeListener no se comporte bien en un entorno concurrente, incluso si se lleva a cabo procesos en línea c-tor (que creo que no es - pensar en la creación de objetos que utilizan la reflexión accediendo c-tor privado).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top