2011-07-15 9 views

Respuesta

3

Este es un caso donde alguien podría pensar que lo que están haciendo está bien, pero probablemente no es lo que pretendían. En este caso, se está sincronizando con el valor actual en la variable obj. Una vez que crea una nueva instancia y la coloca en la variable obj, las condiciones de bloqueo cambiarán. Si eso es todo lo que está ocurriendo en este bloque, probablemente funcione, pero si está haciendo algo más después, el objeto no se sincronizará correctamente.

Mejor para estar seguro y sincronizar en el objeto que contiene, o en otro mutex por completo.

19

Probablemente no sea lo que quiere hacer. Estás sincronizando en un objeto al que ya no tienes referencia. Considere otro hilo que ejecuta este método: pueden ingresar e intentar presionar el candado en el momento después de que se haya actualizado la referencia a obj para señalar el nuevo objeto. En ese punto, están sincronizando en un objeto diferente al primer hilo. Esto probablemente no es lo que estás esperando.

A menos que tenga una buena razón para no hacerlo, es probable que desee sincronizar en un objeto final (para mayor visibilidad). En este caso, es probable que desee utilizar una variable de bloqueo independiente. Por ejemplo:

class Foo 
{ 
    private final Object lock = new Object(); 
    private Object obj; 

    public void method() 
    { 
     synchronized(lock) 
     { 
      obj = new Object(); 
     } 
    } 
} 
+0

+1 - respuesta más completa que la que di :) – aperkins

+0

http://www.javaroots.com/2015/01/java-multithreading-interview-question.html ¿cuál sería el resultado de este código? – somaniA

0

Si obj es una variable local y ningún otro hilo está evaluando con el fin de obtener un bloqueo en él as shown here entonces no importa. Por lo demás este es ineficaz y se aplica lo siguiente:

(Publicación de esto porque las otras respuestas no son lo suficientemente enérgica-- "probablemente" no es suficiente aquí - y no tienen suficientes detalles.)

Cada vez que un hilo encuentra un bloque sincronizado, antes de que pueda adquirir el bloqueo, tiene que descubrir qué objeto necesita bloquear, mediante la evaluación de la expresión en parens que sigue a la palabra clave sincronizada.

Si la referencia se actualiza después de que el hilo evalúa esta expresión, el hilo no tiene forma de saberlo. Procederá a adquirir el bloqueo en el objeto antiguo que identificó como el bloqueo anterior. Finalmente ingresa al bloqueo sincronizado del objeto antiguo, mientras que otro hilo (que intenta ingresar al bloque después de que el bloqueo cambió) ahora evalúa el bloqueo como el nuevo objeto y entra al mismo bloque del mismo objeto que contiene el nuevo bloqueo. y no tiene exclusión mutua.

La sección correspondiente en el JLS es 14.19. El hilo de ejecutar la instrucción sincronizado:

1) evalúa la expresión, entonces

2) adquiere el bloqueo en el valor que la expresión se evalúa, a continuación,

3) ejecuta el bloque.

No vuelve a visitar el paso de evaluación en el momento en que adquiere con éxito el bloqueo.

Este código está roto. No hagas esto Asegúrate de cosas que no cambian.

0

Es un uso poco común pero parece ser válido en los mismos escenarios. Uno que he encontrado en el código base de JmDNS:

public Collection<? extends DNSEntry> getDNSEntryList(String name) { 
    Collection<? extends DNSEntry> entryList = this._getDNSEntryList(name); 
    if (entryList != null) { 
     synchronized (entryList) { 
      entryList = new ArrayList<DNSEntry>(entryList); 
     } 
    } else { 
     entryList = Collections.emptyList(); 
    } 
    return entryList; 
} 

Lo que hace es para sincronizarlas en la lista devuelta por lo que esta lista no quede modificado por otros y luego hace una copia de esta lista. En esta situación especial, el bloqueo solo es necesario para el objeto original.

Cuestiones relacionadas