2011-06-23 7 views
12

¿Alguien puede decirme si esta clase es segura o no?concurrencia de Java: ¿es el campo final (inicializado en el constructor) seguro para subprocesos?

class Foo { 

    private final Map<String,String> aMap; 

    public Foo() { 
     aMap = new HashMap<String, String>(); 
     aMap.put("1", "a"); 
     aMap.put("2", "b"); 
     aMap.put("3", "c"); 
    } 

    public String get(String key) { 
     return aMap.get(key); 
    } 

} 

Editar: Es mi culpa no aclarar la pregunta. De acuerdo con JMM FAQ:

una nueva garantía de la seguridad de inicialización debe ser prestado. Si un objeto está construido correctamente (lo que significa que las referencias a él no se escapan durante la construcción), todos los hilos que ven una referencia a ese objeto también verán los valores para sus campos finales que se establecieron en el constructor, sin la necesidad de sincronización.

Esto me hizo confundir que el conjunto en aMap es aMap = new HashMap<String, String>();. Para que otros hilos puedan ver estos

aMap.put("1", "a"); 
aMap.put("2", "b"); 
aMap.put("3", "c"); 

¿o no?

Edit: encontraron esta question que cierra exactamente a mi pregunta

+1

Lo es, pero la presencia de la palabra clave final no tiene absolutamente nada que ver con eso, por lo que parece que todavía hay confusión. Si explica lo que está esperando que suceda, podremos ayudar más. – Affe

+2

Ah-ha! Mira lo que estás preguntando ahora. Claramente, paso demasiado tiempo en la tierra de los frijoles singleton gestionados. Sí, si leyó toda la sección 17.5 de la especificación del lenguaje Java (que resume el artículo que vinculó), menciona que los objetos a los que se hace referencia en los campos finales también están garantizados al final de la construcción. – Affe

Respuesta

14

Como ya se ha señalado que es absolutamente seguro para subprocesos, y final es importante en este caso debido a sus efectos de visibilidad memoria.

La presencia de final garantiza que otros subprocesos verán los valores en el mapa después de que el constructor finalice sin ninguna sincronización externa.Sin final que no se puede garantizar en todos los casos, y usted tendría que usar modismos publicación seguras al hacer objeto de nueva construcción a disposición de otros temas, a saber (de Java Concurrency in Practice):

  • Inicializar una referencia de objeto de un inicializador estático;
  • Almacenar una referencia en un campo volátil o AtomicReference;
  • Almacenar una referencia en un campo final de un objeto construido correctamente; o
  • Guardando una referencia en un campo que esté debidamente protegido por un candado.
1

Sí lo es, siempre que esta es toda la definición de la clase y no un fragmento del mismo.

La clave es que no hay forma de que los contenidos de aMap puedan modificarse después de la construcción.

+1

En realidad, la palabra clave final es crítica. Consulte http://jeremymanson.blogspot.com/2008/04/immutability-in-java.html para obtener una buena explicación de por qué. –

6

Sí lo es. No hay forma de modificar la referencia aMap, o agregarla al mapa después del constructor (excluyendo la reflexión).

Si expone aMap no lo será, porque dos hilos podrían modificar el mapa al mismo tiempo.

Puede mejorar su clase haciendo aMap no modificable a través de Collections.unmodifiableCollection o Collections.unmodifiableMap.

0

Como es ahora, debería ser seguro para subprocesos. Sin embargo, si agrega otros métodos que modifican el hashmap, entonces no.

1

Esta clase no tiene problemas de concurrencia porque expone solo un método get. Si agrega algún método que modifique el mapa, debe marcar este método como synchronized.

2

Guava tiene clases inmutables para hacer este tipo de cosas más fácil y garantizado inmutable:

private final ImmutableMap<String, String> aMap = ImmutableMap.of(
    "1", "a", 
    "2", "b", 
    "3", "c"); 
0

no creo que el fragmento de código anterior es seguro para subprocesos. La única línea que es el código de seguridad es

aMap = new HashMap<String, String>(); 

Como por ejemplo dado en http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html,

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; // x is guaranteed to be 3 
     int j = f.y; // y can have any value 
    } 
    } 
} 

Esto significa que una vez que los campos finales se inicializan no hay hilo de seguridad garantizada. Dado que solo la asignación de referencia está garantizada para ser segura para hilos y el objeto en sí mismo puede ser mutable según su ejemplo. Tras la declaración podría no ser seguro para subprocesos

aMap.put("1", "a"); 
aMap.put("2", "b"); 
aMap.put("3", "c"); 

EDITAR Mi mala vio los comentarios a continuación el código más tarde

La capacidad de ver el valor correctamente construida para el campo es bonito, pero si el campo en sí mismo es una referencia, luego también quiere que su código vea los valores actualizados para el objeto (o matriz) a los que apunta. Si su campo es un campo final, esto también está garantizado. Por lo tanto, puede tener un puntero final a una matriz y no tener que preocuparse de que otros subprocesos vean los valores correctos para la referencia de matriz, pero valores incorrectos para los contenidos de la matriz. De nuevo, por "correcto" aquí, queremos decir "actualizado a partir del final del constructor del objeto", no "el último valor disponible".

Cuestiones relacionadas