¿Son realmente útiles los campos finales con respecto a la seguridad del hilo?

He estado trabajando diariamente con el Modelo de Memoria Java durante algunos años. Creo que entiendo bien el concepto de carreras de datos y las diferentes formas de evitarlas (por ejemplo, bloques sincronizados, variables volátiles, etc.). Sin embargo, todavía hay algo que no creo que entienda completamente sobre el modelo de memoria, que es la forma en que se supone que los campos finales de clases son seguros para subprocesos sin ninguna sincronización adicional.

Entonces, de acuerdo con la especificación, si un objeto se inicializa correctamente (es decir, no se escapa ninguna referencia al objeto en su constructor de tal manera que otro hilo pueda ver la referencia), luego, después de la construcción, cualquier hilo que vea el Se garantizará que el objeto vea las referencias a todos los campos finales del objeto (en el estado en que estaban cuando se construyeron), sin ninguna sincronización adicional.

En particular, el estándar ( http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 ) dice:

El modelo de uso para los campos finales es simple: establezca los campos finales para un objeto en el constructor de ese objeto; y 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 esté terminado. Si se sigue esto, cuando otro objeto vea el hilo, ese hilo siempre verá la versión construida correctamente de los campos finales de ese objeto. También verá versiones de cualquier objeto o matriz referenciada por los campos finales que estén al menos tan actualizados como los campos finales.

Incluso dan el siguiente ejemplo:

class FinalFieldExample { 
    final int x;
    int y; 
    static FinalFieldExample f;

    public FinalFieldExample() {
        x = 3; 
        y = 4; 
    } 

    static void writer() {
        f = new FinalFieldExample();
    } 

    static void reader() {
        if (f != null) {
            int i = f.x;  // guaranteed to see 3  
            int j = f.y;  // could see 0
        } 
    } 
}

En el que se supone que un subproceso A ejecuta "reader ()", y un subproceso B debe ejecutar "writer ()".

Hasta ahora, todo bien, aparentemente.

Mi principal preocupación tiene que ver con ... ¿es esto realmente útil en la práctica? Hasta donde sé, para que el hilo A (que ejecuta "reader ()") vea la referencia a "f", debemos usar algún mecanismo de sincronización, como hacer que f sea volátil o usar bloqueos para sincronizar el acceso a F. Si no lo hacemos, ni siquiera estamos garantizados de que "reader ()" pueda ver una "f" inicializada, es decir, dado que no tenemos acceso sincronizado a "f", el lector potencialmente verá " nulo "en lugar del objeto que fue creado por el hilo del escritor. Este problema se indica en http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#finalWrong , que es una de las principales referencias para el Java Memory Model [negrita énfasis mío ]:

Ahora, habiendo dicho todo esto, si, después de que un hilo construye un objeto inmutable (es decir, un objeto que solo contiene campos finales), desea asegurarse de que todos los otros hilos lo vean correctamente, todavía necesita para usar la sincronización. No hay otra manera de asegurar, por ejemplo, que la referencia al objeto inmutable sea vista por el segundo hilo . Las garantías que obtiene el programa de los campos finales se deben moderar cuidadosamente con una comprensión profunda y cuidadosa de cómo se gestiona la concurrencia en su código.

Entonces, si ni siquiera se nos garantiza ver la referencia a "f", y por lo tanto debemos usar mecanismos de sincronización típicos (volátiles, bloqueos, etc.), y estos mecanismos ya causan que las carreras de datos desaparezcan, la necesidad de final es algo que ni siquiera consideraría. Quiero decir, si para hacer que "f" sea visible para otros hilos aún necesitamos usar bloques volátiles o sincronizados, y ellos ya hacen que los campos internos sean visibles para los otros hilos ... cuál es el punto (en términos de seguridad de hilos) en haciendo una final de campo en primer lugar?

Respuesta 1

Estoy desarrollando una aplicación web java y eso incluye un applet. Ese applet depende de dos archivos jar: JFreeChart (para trazar gráficos en el lado del cliente) - 1.7 mb (tamaño del jar ...

Digamos que tengo el siguiente conjunto de cadenas. Cadena [] arr = {"Índice0", "Índice1", "Índice2", "Índice3", ... "Índice n"}; Puedo iterar sobre toda la matriz usando: for (String eachElement: arr) {// ...

Soy nuevo en StackOverflow y he odiado la idea de hacer una pregunta, ya que he visto tantos derribados en llamas que me preguntaron mal o respondieron en otro lado, pero parece que no puedo encontrar un ...

Por defecto, la versión requerida de Hamcrest para: JUnit 4.11 Hamcrest 1.3 Mockito-core 1.9.5 Hamcrest 1.1 No hubo cambios de API insignificantes entre Hamcrest 1.1 y 1.3. Actualmente mi prueba ...