Objeto de locking estático frente a no estático en bloque sincronizado

Intentando visualizar y entender la sincronización .

  1. ¿Cuáles son las diferencias entre usar un objeto de locking estático (código A) y un objeto de locking no estático (código B) para un bloque sincronizado ?
  2. ¿Cómo difiere en las aplicaciones prácticas?
  3. ¿Cuáles son las dificultades que uno tendría que el otro no tendría?
  4. ¿Cuáles son los criterios para determinar cuál usar?

Código A

public class MyClass1 { private static final Object lock = new Object(); public MyClass1() { //unsync synchronized(lock) { //sync } //unsync } } 

Código B

 public class MyClass2 { private final Object lock = new Object(); public MyClass2() { //unsync synchronized(lock) { //sync } //unsync } } 

Nota

El código anterior muestra constructores, pero podría hablar sobre cómo el comportamiento es diferente en un método estático y no también estático. Además, ¿sería ventajoso usar un locking estático cuando el bloque sincronizado está modificando una variable miembro estática?

Ya miré las respuestas en esta pregunta , pero no está suficientemente claro cuáles son los diferentes escenarios de uso.

La diferencia es simple: si el objeto encerrado está en un campo static , todas las instancias de MyClass* compartirán ese locking (es decir, no habrá dos objetos que puedan bloquear ese objeto al mismo tiempo).

Si el campo no es estático, cada instancia tendrá su propio locking, por lo que solo las llamadas al método en el mismo objeto se bloquearán entre sí.

Cuando usas un objeto de locking estático:

  • el hilo 1 llama a o1.foo()
  • el hilo 2 llama a o1.foo() , tendrá que esperar a que termine el hilo 1
  • el hilo 3 llama a o2.foo() , también tendrá que esperar a que termine el hilo 1 (y probablemente 2)

Cuando utiliza un objeto de locking no estático:

  • el hilo 1 llama a o1.foo()
  • el hilo 2 llama a o1.foo() , tendrá que esperar a que termine el hilo 1
  • thread 3 llama a o2.foo() , simplemente puede continuar, sin importar el thread 1 y 2

El que necesite dependerá del tipo de datos que intente proteger con su bloque sincronizado.

Como regla general, quiere que el objeto de locking tenga el mismo valor static que el valor operado. Por lo tanto, si manipula solo valores no estáticos, querrá un objeto de locking no estático. Si manipula solo valores estáticos, querrá un objeto de locking estático.

Cuando manipulas valores estáticos y no estáticos , se volverá complicado. La manera más sencilla sería utilizar un objeto de locking estático, pero eso podría boost el tamaño del bloque sincronizado más de lo absolutamente necesario y podría necesitar bloquear más la contención de lo deseado. En esos casos, es posible que necesite una combinación de objetos de locking estáticos y no estáticos.

En su caso particular, utiliza el locking en el constructor, que solo se ejecutará una vez por instancia, por lo que un objeto de locking no estático no tiene sentido aquí.