threads synchronizable sincronizar sincronizados sincronizadas productores mutua hilos exclusion consumidores clases java multithreading concurrency thread-safety

synchronizable - Clase Thread-safe en Java mediante bloques sincronizados



synchronizable java (2)

Digamos que tenemos una clase de Java muy simple, MyClass .

public class MyClass { private int number; public MyClass(int number) { this.number = number; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } }

Hay tres formas de construir una clase de Java sin hilos que tiene algún estado:

  1. Hazlo verdaderamente inmutable

    public class MyClass { private final int number; public MyClass(int number) { this.number = number; } public int getNumber() { return number; } }

  2. Hacer que el number campo sea volatile .

    public class MyClass { private volatile int number; public MyClass(int number) { this.number = number; } public int getNumber() { return number; } public void setNumber(int number) { this.number = number; } }

  3. Use un bloque synchronized . La versión clásica de este enfoque se describe en el Capítulo 4.3.5 de la concurrencia de Java en la práctica. Y lo curioso es que tiene un error en el ejemplo que se menciona en una errata para este libro.

    public class MyClass { private int number; public MyClass(int number) { setNumber(number); } public synchronized int getNumber() { return number; } public synchronized void setNumber(int number) { this.number = number; } }

Hay un hecho más que debe agregarse al contexto de discusión. En un entorno multiproceso, la JVM puede reordenar las instrucciones fuera del bloque synchronized conservando una secuencia lógica y las relaciones pasadas antes de las especificadas por JVM. Puede causar la publicación de objetos que no están construidos correctamente para otro hilo.

Tengo un par de preguntas sobre el tercer caso.

  1. Va a ser equivalente a un siguiente fragmento de código:

    public class MyClass { private int number; public MyClass(int number) { synchronized (this){ this.number = number; } } public synchronized int getNumber() { return number; } public synchronized void setNumber(int number) { this.number = number; } }

  2. ¿Se evitará un reordenamiento en el tercer caso o es posible que JVM reordene las instrucciones y, por lo tanto, publique el objeto con el valor predeterminado en el number campo?

  3. Si la respuesta a la segunda pregunta es sí, entonces tengo una pregunta más.

    public class MyClass { private int number; public MyClass(int number) { synchronized (new Object()){ this.number = number; } } public synchronized int getNumber() { return number; } public synchronized void setNumber(int number) { this.number = number; } }

Este aspecto extraño synchronized (new Object()) se supone que evita el efecto de reordenación. ¿Funcionará?

Para que quede claro, todos estos ejemplos no tienen ninguna aplicación práctica. Solo tengo curiosidad sobre los matices del multihilo.


En la pregunta n. ° 3, synchronized(new Object()) es un no-operativo y no impedirá nada. El compilador puede determinar que ningún otro subproceso podría sincronizarse en ese objeto (ya que nada más puede acceder al objeto). Este es un ejemplo explícito en el artículo de Brian Goetz " Teoría y práctica de Java: Optimizaciones de sincronización en Mustang ".

Incluso si necesitaras sincronizar en un constructor, e incluso si tu bloque synchronized(new Object()) fuera útil, es decir, estuvieras sincronizando en un objeto diferente de larga duración, ya que tus otros métodos se están sincronizando en this , tienes problemas de visibilidad si no está sincronizando en la misma variable. Es decir, realmente quiere que su constructor también use synchronized(this) .

Un lado:

La sincronización en this se considera de forma pobre. En cambio, sincronízate en algún campo final privado. Las personas que llaman pueden sincronizarse en su objeto, lo que podría llevar a un punto muerto. Considera lo siguiente:

public class Foo { private int value; public synchronized int getValue() { return value; } public synchronized void setValue(int value) { this.value = value; } } public class Bar { public static void deadlock() { final Foo foo = new Foo(); synchronized(foo) { Thread t = new Thread() { public void run() { foo.setValue(1); } }; t.start(); t.join(); } } }

No es obvio para quienes llaman de la clase Foo que esto empantanaría. Lo mejor es mantener su semántica de bloqueo interna y privada para su clase.


synchronized(new Object()) no hará nada, ya que la sincronización solo se realiza en el objeto en el que se sincroniza. Por lo tanto, si el hilo A se sincroniza en oneObject , y el hilo B se sincroniza en otro anotherObject , no pasa nada antes entre ellos. Dado que podemos saber con certeza que ningún otro hilo se sincronizará en el new Object() crees allí, esto no establecerá un happening previo entre cualquier otro hilo.

En cuanto a su synchronzied en el constructor, si su objeto se publica de forma segura en otro hilo, no lo necesita; y si no es así, probablemente estés en un lío de problemas como es. Hice esta pregunta en la lista de interés concurrente hace un momento, y resultó un hilo interesante . Ver en particular este correo electrónico , que señala que incluso con su constructor sincronizado, a falta de publicación segura, otro hilo podría ver valores predeterminados en sus campos, y este correo electrónico (que) une todo el asunto.