La comprensión en profundidad del modelo de memoria de Java (seis) - Final

En comparación con el bloqueo anteriormente descrito y volátil, leer y escribir en los campos finales, como el acceso variable ordinaria. Para el dominio final, los compiladores y procesadores para cumplir con dos reglas de reordenación:

  1. Escrito en el constructor de un campo final, y después el objeto referenciado esta configuración asignada a una variable de referencia, no reordenamiento entre estas dos operaciones.
  2. Lectura de un primer dominio diana que comprende una referencia final y, a continuación, leer el campo inicial final, el reordenamiento no es entre estas dos operaciones.

A continuación, se pasa un código de ejemplo para ilustrar estas dos reglas:

public class FinalExample {
    int i;                            // 普通变量 
    final int j;                      //final 变量 
    static FinalExample obj;

    public void FinalExample () {     // 构造函数 
        i = 1;                        // 写普通域 
        j = 2;                        // 写 final 域 
    }

    public static void writer () {    // 写线程 A 执行 
        obj = new FinalExample ();
    }

    public static void reader () {       // 读线程 B 执行 
        FinalExample object = obj;       // 读对象引用 
        int a = object.i;                // 读普通域 
        int b = object.j;                // 读 final 域 
    }
}

Se supone que un método ejecuta hilo escritor A (), seguido de otro hilo B método Reader () ejecutado. A continuación nos relacionamos a través de estos dos hilos para ilustrar estas dos reglas.

Escribir reordenamiento gobierna dominio definitivo

reglas de reordenación de campo finales escritos de escritura prohíben la reordenación de campo final fuera del constructor. La norma de aplicación contiene los dos aspectos siguientes:

  • JMM prohíbe el compilador para escribir la reordenación de campo final fuera del constructor.
  • Antes de que el compilador escribirá después del último campo, los constructores regresan, insertar una barrera StoreStore. Esta barrera está prohibido procesador para escribir la reordenación de campo final fuera del constructor.

Veamos método escritor () ahora analizamos. escritor () método incluye sólo una línea de código: finalExample = nuevo FinalExample (). Este código incluye dos pasos:

  1. FinalExample construyó tipo de objeto;
  2. La asignación de este objeto de referencia a la variable de referencia obj.

Hilo B lee asume ninguna reordenación referencia de objeto y leer entre los miembros del objeto de dominio (inmediatamente explicar por qué este supuesto), a continuación es una posible secuencia de ejecución:

En la figura, las operaciones de escritura son dominio común desalentó ordenó compilar una fuera de hilo constructor B lee el valor de las lecturas erróneas de fricción antes de la inicialización de la variable i. Operación de final escrito es re-escrito de dominio cotejo final "se define" dentro del constructor, el hilo B lee el valor de lectura correcta después de la inicialización última variable.

campo final escrita reordenación de reglas para garantizar que: antes de cualquier tema puede ser visto como una referencia, objeto de dominio definitivo ya se ha inicializado correctamente, mientras que el dominio ordinario no tiene esta garantía en el objeto. La figura anterior como ejemplo, en el lector de hilo B "ver" cuando el obj referencia de objeto, objeto obj probablemente aún no se han construido (i escribir operación para el dominio común para ser reordenado a constructores, cuando el valor inicial no está escrito todavía 2 dominios comunes i).

Leer el dominio final de reordenamiento regla

Reordenar el campo de lectura normas finales son los siguientes:

  • En un hilo, la referencia al objeto primario con la lectura inicial leído del campo final contiene objetos, el procesador está prohibido JMM reordenación de dos operaciones (Tenga en cuenta que esta regla sólo para el procesador). LoadLoad compilador inserta una barrera frente a las operaciones de lectura-dominio finales.

Primera lectura y el campo de referencia objeto final lectura inicial contiene el objeto, una dependencia indirecta entre las dos operaciones. Desde las dependencias indirectas de cumplimiento del compilador, el compilador hace una reordenación de estas dos operaciones. La mayoría de los procesadores estarán eternamente indirectamente dependientes, la mayoría de los procesadores no van a cambiar el orden de estas dos operaciones. Sin embargo, algunos de los procesador permite al operador hacer una dependencia indirecta de reordenamiento (tal como alfa procesador), esta regla está diseñado para para tales procesadores.

método Reader () incluye tres operaciones:

  1. Primera lectura variable de referencia obj;
  2. Referencia inicial al leer puntos j obj ordinarios dominio variable a un objeto.
  3. Primero leyó referencia variable que apuntan objeto obj campos finales i.

Ahora asumimos que el hilo escritor A no tiene ningún reordenamiento se produce, mientras que en el incumplimiento de la ejecución del programa depende indirectamente en el procesador, la siguiente es una posible secuencia de ejecución:

En la figura, la operación de lectura de un dominio objeto ordinario por referencias Reordenar procesador a los objetos antes de leer. Leer dominio ordinario, que no ha sido escrita hilo Un escrito, se trata de una operación de lectura incorrecta. campos finales leen Reordenar las reglas diana operación campos finales leerá "definido" Después de leer referencia de objeto, en cuyo momento el campo final se ha inicializado el hilo A, que es una operación de lectura correcto.

La lectura de las reglas finales campo de reordenamiento para asegurar que: antes de la lectura final de un dominio objeto, en primer lugar leerá el campo final contiene una referencia de objeto. En este programa de ejemplo, si la referencia no es nulo, entonces el dominio final de referencia de objeto debe haber sido inicializado Un hilo antes.

Si el último campo es un tipo de referencia

Arriba vemos el campo final es el tipo de datos subyacente, vamos a ver si el último campo es un tipo de referencia, tendrá qué efecto?

Considere el siguiente código de ejemplo:

public class FinalReferenceExample {
final int[] intArray;                     //final 是引用类型 
static FinalReferenceExample obj;

public FinalReferenceExample () {        // 构造函数 
    intArray = new int[1];              //1
    intArray[0] = 1;                   //2
}

public static void writerOne () {          // 写线程 A 执行 
    obj = new FinalReferenceExample ();  //3
}

public static void writerTwo () {          // 写线程 B 执行 
    obj.intArray[0] = 2;                 //4
}

public static void reader () {              // 读线程 C 执行 
    if (obj != null) {                    //5
        int temp1 = obj.intArray[0];       //6
    }
}
}

Aquí campo final es un tipo de referencia, que las referencias de una matriz objeto de tipo int. Para los tipos de referencia, el campo de escritura reordenación definitiva gobierna el compilador y el procesador añade la siguiente restricción:

  1. Escrito en la función de objeto constructor del miembro de referencia final de un dominio, y luego este se hace referencia en la configuración fuera de la función objeto constructor asignado a una variable de referencia, no reordenamiento entre estas dos operaciones.

El procedimiento anterior de ejemplo, se supone que se ejecuta el método primera hilo A writerOne ejecutado () después de que el hilo B ejecutado writerTwo () método se ejecuta después de que el hilo C ejecuta el método () Reader. Aquí es una posible temporización de la ejecución del hilo:

En la figura, 1 es escrito en el campo final, 2 se escribe en el último miembro de un dominio del objeto de dominio de referencia, 3 es la estructura de la referencia de objeto se asigna a una variable de referencia. Aquí, además de 1 y 3 no se puede cambiar el orden de lo anterior, 2 y 3 no se pueden reordenar.

JMM asegura que el hilo lector de C, al menos, ser capaz de ver los miembros del dominio de escritura hilo escribir una referencia al objeto en el constructor para la final. Es decir 0 C para ver al menos en la matriz se marca 1. El hilo de escritura B a un elemento de matriz está escrito, el hilo lector podría mirar a C, no se puede ver. JMM no se garantiza que leer la escritura hilo de rosca B C visible porque hay competencia entre los datos leídos e hilo de rosca de escritura B C, en este momento los resultados son impredecibles.

Si desea asegurarse de que lectura y escritura hilo de rosca B C Consulte los elementos de la matriz de escribir, escritura, requiere el uso de primitivas de sincronización (bloqueo o volátiles) entre los hilos B y C para asegurar que la lectura de la memoria hilos visibilidad.

¿Por qué cita final no puede "escapar" de dentro del constructor

Como se mencionó anteriormente, el campo final escrito reordenación de reglas para asegurar que: antes de cualquier hilo es visible en las variables de referencia a ese punto el objeto de dominio variable de referencia final se ha inicializado correctamente en el constructor. De hecho, para conseguir este efecto, también necesitamos una garantía: en el constructor no puede que esto sea una referencia al objeto construido visible para otros hilos, es decir, las referencias a objetos no pueden "escapar" en el constructor. Para ilustrar el problema, vamos a ver el código de ejemplo siguiente:

public class FinalReferenceEscapeExample {
final int i;
static FinalReferenceEscapeExample obj;

public FinalReferenceEscapeExample () {
    i = 1;                              //1 写 final 域 
    obj = this;                          //2 this 引用在此“逸出”
}

public static void writer() {
    new FinalReferenceEscapeExample ();
}

public static void reader {
    if (obj != null) {                     //3
        int temp = obj.i;                 //4
    }
}
}

 hilo Supongamos que A ejecuta un método escritor (), otro hilo B ejecuta el método Reader (). 2, de modo que el objeto operación está aquí configurado como visible antes de hilo B no se ha completado. 2 aunque la operación es el último paso en el que el constructor, método incluso si el operador 2 y una operación de fila trasera, la realización de leer () de la rosca todavía no puede ver el valor del campo final se inicializa, porque la operación en el programa en el que una operación entre 2 y puede ser reordenado. El momento actual se puede realizar como se muestra a continuación:

De la figura podemos ver que: antes de que el constructor devuelve el objeto referenciado no es visible para otros hilos configurados, al final, ya que el dominio no se ha inicializado. Después de las declaraciones de constructor, se garantizará ningún hilo para ver el valor después del último campo ha inicializado correctamente.

final de Semántica implementado en un procesador

Ahora que el procesador x86, por ejemplo, la semántica específica para lograr procesador final.

Hemos mencionado anteriormente, escribimos se les pedirá Reordenar las reglas finales de campo para interpretar ed por escrito después de que el último campo, el retorno constructor antes de insertar una pantalla de barrera StoreStore. reglas de reordenación finales de lectura de campo requieren los insertos compilador una barrera final LoadLoad frente al dominio operación de lectura.

Debido a que los procesadores x86 no escribirá - reordenamiento de escritura hacen de procesadores x86, escribir se omitirá la pantalla barrera StoreStore campo necesaria final. Además, no ya que el funcionamiento del procesador x86 es un dependencias indirectas no reordenando, por lo que el procesador x86, la barrera de lectura LoadLoad tendrán que ser omitido campos finales. Ese es el procesador x86, la lectura final del dominio / escritura no inserta ninguna barrera de memoria!

¿Por JSR-133 para mejorar la final semántica

En el viejo modelo de memoria de Java, el defecto más grave es un hilo podría ver el valor cambiará campos finales. Por ejemplo, cuando un hilo está viendo actualmente un valor de 0 (el valor por defecto antes aún inicializado) un campo final de la cirugía plástica, después de un período de tiempo para ir a leer este valor final de hilo de este campo, pero se encontró que el valor se establece en 1 (siendo una valor después de la inicialización de la rosca). El ejemplo más común es el viejo modelo de memoria de Java, el valor de la cadena puede cambiar (Ref. 2, hay un ejemplo concreto, el lector interesado puede referirse a sí mismos, no entrar en detalles aquí).

Para solucionar esta vulnerabilidad, JSR-133 grupo de expertos mejora la final semántica. Al aumentar el campo de las reglas finales de lectura y escritura y re-ordenamiento, la inicialización puede proporcionar seguridad para los programadores de Java: Mientras se construye el objeto correctamente (no citó "escape" en el objeto construido constructor), no es necesario utilizar síncrona (se refiere a la utilización de cerradura y volátil), puede asegurarse que cualquier hilo puede ver el valor después del campo final se inicializa en el constructor.

Publicados 136 artículos originales · ganado elogios 6 · vistas 1477

Supongo que te gusta

Origin blog.csdn.net/weixin_42073629/article/details/104741742
Recomendado
Clasificación