2010-07-30 14 views
40

Conozco la diferencia entre el método sincronizado y el bloque sincronizado, pero no estoy seguro acerca de la parte sincronizada del bloque.¿Cuál es la diferencia entre sincronizado en lockObject y usando esto como el bloqueo?

Suponiendo que tengo este código

class Test { 
    private int x=0; 
    private Object lockObject = new Object(); 

    public void incBlock() { 
    synchronized(lockObject) { 
     x++; 
    } 
    System.out.println("x="+x); 
    } 

    public void incThis() { // same as synchronized method 
    synchronized(this) { 
     x++; 
    } 
    System.out.println("x="+x); 
    } 
} 

En este caso, ¿cuál es la diferencia entre usar cuadro LockObject y el uso de este como la cerradura? Parece ser lo mismo para mí ...

Cuando decide utilizar el bloque sincronizado, ¿cómo decide qué objeto será el bloqueo?

+5

he encontrado la respuesta a mi pregunta en http://stackoverflow.com/questions/3047564/java-synchronized-method-lock-on-object-or-method Si nos fijamos en la respuesta, es muy claro que si hay 2 hilos (t1 y t2) donde t1 llama a x.addA() y t2 llama a x.addB(). Si tanto addA como addB usan esto como el bloqueo, entonces x.addA() y x.addB() no pueden ejecutarse al mismo tiempo. Considerando que si addA y addB utilizan un objeto diferente para el bloqueo, ambos métodos pueden ejecutarse simultáneamente – GantengX

+2

Solo para verificar algo: En el ejemplo anterior, cuando se usa lockObject para proteger eficazmente el acceso a x, ¿no hay una situación donde otro hilo podría entrar después del bloque sincronizado pero antes de la impresión y aumentar x de nuevo? Es decir. ¿realmente necesita la impresión dentro del bloque sincronizado? –

+1

ah cierto, debería haberlo puesto en el bloque sincronizado, pero ya he respondido mi propia pregunta: D – GantengX

Respuesta

54

Personalmente, casi nunca me fijo en "esto". Normalmente me fijo en una referencia privada que sé que ningún otro código va a bloquear. Si bloquea "this" entonces cualquier otro código que conozca su objeto puede elegir bloquearlo. Si bien es poco probable que suceda, sin duda podría hacerlo, y podría causar bloqueos o un bloqueo excesivo.

No hay nada particularmente mágico en lo que encierras, puedes pensarlo como una ficha, efectivamente. Cualquiera que trabaje con la misma ficha intentará adquirir el mismo bloqueo. A menos que desee otro código para poder adquirir el mismo candado, use una variable privada. Me gustaría también le animo a hacer la variable final - No puedo recordar una situación en la que alguna vez quería cambiar una variable de bloqueo durante la vida útil de un objeto.

+0

¿Puedes elaborar más? Decir clase 'A' que tiene instancia de clase' B'. Ahora 'B' tiene alguna variable miembro. Cuando 'A' llama a cualquier método de' B', se bloquea en este/miembro de instancia ... ¿verdad? Medios se bloquea en la instancia de 'B' en sí o en su miembro ... ¿puedes explicar la diferencia? – Parth

+1

@Paarth: Su comentario no es claro. ¿Quiere decir * debería * 'B' bloquear en el bloqueo privado para cada método? Eso depende del propósito de la clase en sí. La mayoría de las clases no necesitan tratar de ser seguras para subprocesos, para ser honestos. –

+0

yah, entonces necesito hacer un método de sincronización, shuold use el objeto privado miembro en lugar de 'esto' ... ¿verdad? ok ... – Parth

0

En este caso, no importa qué objeto elija para el bloqueo. Pero debe usar consistentemente el mismo objeto para bloquear para lograr la sincronización correcta. El código anterior no garantiza la sincronización adecuada ya que una vez usa el objeto 'this' como candado y luego el 'lockObject' como candado.

+0

Entiendo que no se trata de una sincronización adecuada, ya que usa un bloqueo diferente para un método diferente, solo trato de entender la diferencia entre usar 'esto' y 'lockObject' para el bloqueo – GantengX

+0

oh .. disculpa por malentender tu pregunta. Entonces, para eso, la respuesta sería la misma que Jon Skeet ha respondido. – Gopi

4

El artículo 67 de Effective Java Second Edition es Evitar la sincronización excesiva, por lo tanto, me sincronizaría en un objeto de bloqueo privado.

+0

No responde la pregunta (use 'sincronizado' o' Bloqueos'), no hay explicación real, enlace roto ([versión archivada] (http://web.archive.org/web/20090904194857/http://java. sun.com/docs/books/effective/index.html)). Downvoted. –

1

Cada objeto en Java puede actuar como un monitor. Elegir uno depende de la granularidad que desee. Elegir 'esto' tiene la ventaja y la desventaja de que otras clases también podrían sincronizarse en el mismo monitor. Sin embargo, mi consejo es evitar el uso directo de la palabra clave synchronize y, en su lugar, usar constructos de la biblioteca java.util.concurrency, que son de nivel superior y tienen una semántica bien definida. Este libro tiene un montón de buenos consejos en él de los expertos muy notables:

Java concurrencia en la práctica http://amzn.com/0321349601

9

tuve esta misma pregunta cuando estaba leyendo Java concurrencia en la práctica, y pensé que me gustaría añadir una perspectiva adicional sobre las respuestas proporcionadas por Jon Skeet y spullara.

Aquí hay un código de ejemplo que bloqueará incluso los métodos "rápidos" setValue(int)/getValue() mientras se ejecuta el método doStuff(ValueHolder).

public class ValueHolder { 
    private int value = 0; 

    public synchronized void setValue(int v) { 
     // Or could use a sychronized(this) block... 
     this.value = 0; 
    } 

    public synchronized int getValue() { 
     return this.value; 
    } 
} 

public class MaliciousClass { 

    public void doStuff(ValueHolder holder) { 
     synchronized(holder) { 
      // Do something "expensive" so setter/getter calls are blocked 
     } 
    } 
} 

La desventaja de usar this para la sincronización es otras clases pueden sincronizar en una referencia a su clase (no a través this, por supuesto).El uso malintencionado o no intencionado de la palabra clave synchronized al bloquear la referencia de su objeto puede hacer que su clase se comporte mal con el uso simultáneo, ya que una clase externa puede bloquear eficazmente sus this métodos sincronizados y no hay nada que pueda hacer (en su clase) para prohibir esto en tiempo de ejecución. Para evitar este peligro potencial, se sincronizaría en un private final Object o utilizaría la interfaz Lock en java.util.concurrent.locks.

Para este ejemplo simple, puede usar alternativamente un AtomicInteger en lugar de sincronizar el setter/getter.

Cuestiones relacionadas