¿Cuál es la diferencia entre sincronizado en lockObject y usando esto como el locking?

Sé la diferencia entre el método sincronizado y el bloque sincronizado, pero no estoy seguro de la parte sincronizada del bloque.

Asumiendo 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 lockObject y usar esto como el locking? Parece ser lo mismo para mí …

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

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”, 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 lockings o un locking 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 locking. A menos que desee que otro código pueda adquirir el mismo locking, use una variable privada. También te animo a que hagas que la variable sea final . No recuerdo una situación en la que alguna vez haya deseado cambiar una variable de locking durante la vigencia de un objeto.

Tenía la misma pregunta cuando leía Java Concurrency In Practice, y pensé que agregaría una perspectiva adicional sobre las respuestas proporcionadas por Jon Skeet y spullara.

Aquí hay un código de ejemplo que bloqueará incluso los setValue(int) “rápidos” de 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 utilizar this para la sincronización es que otras clases pueden sincronizarse en una referencia a su clase (no a través de 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 métodos sincronizados y no hay nada que pueda hacer (en su clase) para prohibir esto en tiempo de ejecución. Para evitar esta trampa potencial, se sincronizaría en un private final Object o usaría la interfaz de Lock en java.util.concurrent.locks .

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

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

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 contiene muchos buenos consejos de expertos muy notables:

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

En este caso, no importa qué objeto elija para el locking. 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.