¿Existe alguna ventaja de utilizar un Método sincronizado en lugar de un Bloque sincronizado?

401

¿Alguien puede decirme la ventaja del método sincronizado sobre el bloque sincronizado con un ejemplo?

Guerrero
fuente
1
@cletus esta pregunta es completamente diferente de stackoverflow.com/questions/442564/…
Yukio Fukuzawa

Respuestas:

431

¿Alguien puede decirme la ventaja del método sincronizado sobre el bloque sincronizado con un ejemplo? Gracias.

No existe una clara ventaja de utilizar el método sincronizado sobre el bloque.

Quizás el único (pero no lo llamaría una ventaja) es que no necesita incluir la referencia del objeto this.

Método:

public synchronized void method() { // blocks "this" from here.... 
    ...
    ...
    ...
} // to here

Bloquear:

public void method() { 
    synchronized( this ) { // blocks "this" from here .... 
        ....
        ....
        ....
    }  // to here...
}

¿Ver? Ninguna ventaja en absoluto.

Los bloques no tienen ventajas sobre los métodos, sin embargo, sobre todo en la flexibilidad porque se puede utilizar como otro objeto de bloqueo, mientras que la sincronización del método encerraría todo el objeto.

Comparar:

// locks the whole object
... 
private synchronized void someInputRelatedWork() {
    ... 
}
private synchronized void someOutputRelatedWork() {
    ... 
}

vs.

// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();

private void someInputRelatedWork() {
    synchronized(inputLock) { 
        ... 
    } 
}
private void someOutputRelatedWork() {
    synchronized(outputLock) { 
        ... 
    }
}

Además, si el método crece, aún puede mantener la sección sincronizada separada:

 private void method() {
     ... code here
     ... code here
     ... code here
    synchronized( lock ) { 
        ... very few lines of code here
    }
     ... code here
     ... code here
     ... code here
     ... code here
}
OscarRyz
fuente
44
Un beneficio para el consumidor de la API es que el uso de la palabra clave sincronizada en la declaración del método también declara explícitamente que el método se sincroniza en la instancia del objeto y es (presumiblemente) seguro para subprocesos.
Scrubbie
59
Sé que esta es una vieja pregunta, pero la sincronización en "esto" se considera en algunos círculos como un antipatrón. La consecuencia involuntaria es que fuera de la clase, alguien puede bloquear una referencia de objeto que sea igual a "esto" y evitar que otros hilos pasen las barreras dentro de la clase creando potencialmente una situación de punto muerto. Crear un "Objeto final privado = nuevo Objeto ();" Variable puramente para fines de bloqueo es la solución de uso frecuente. Aquí hay otra pregunta relacionada directamente con este problema.
justin.hughey
30
"mientras que sincronizar el método bloquearía la clase completa". Esto no es correcto. No bloquea la clase completa, sino la instancia completa. Varios objetos de la misma clase tienen todo su propio candado. :)
Saludos
44
Algo interesante sobre esto es que el uso de un método sincronizado hará que el bytecode generado tenga 1 instrucción menos, ya que los métodos tienen un bit sincronizado en su firma. Dado que la longitud del código de bytes es un factor para determinar si un método se alinea, mover el bloque a la firma del método podría ser la diferencia en la decisión. En teoría de todos modos. No basaría una decisión de diseño en una sola instrucción de bytecode que se guarda, eso parece una idea terrible. Pero aún así, es una diferencia. =)
corsiKa
2
@corsiKa: guarda más de una instrucción. Un synchronizedbloque se implementa utilizando dos instrucciones monitorentery monitorexit, además , un controlador de excepciones que garantiza que monitorexitse llame incluso en el caso excepcional. Todo eso se guarda al usar un synchronizedmétodo.
Holger
139

La única diferencia real es que un bloque sincronizado puede elegir en qué objeto se sincroniza. Un método sincronizado solo puede usar 'this'(o la instancia de clase correspondiente para un método de clase sincronizado). Por ejemplo, estos son semánticamente equivalentes:

synchronized void foo() {
  ...
}

void foo() {
    synchronized (this) {
      ...
    }
}

Este último es más flexible ya que puede competir por el bloqueo asociado de cualquier objeto, a menudo una variable miembro. También es más granular porque podría tener código concurrente ejecutándose antes y después del bloque pero aún dentro del método. Por supuesto, podría usar un método sincronizado fácilmente refactorizando el código concurrente en métodos separados no sincronizados. Use lo que haga que el código sea más comprensible.

jcrossley3
fuente
Este último también puede tener mérito si no todo el código en foo () necesita ser sincronizado.
Evan
1
Esto es cierto, pero no es lo que "Guerrero" preguntó: "La ventaja del método sincronizado" no existe.
OscarRyz
76

Método sincronizado

Pros:

  • Su IDE puede indicar los métodos sincronizados.
  • La sintaxis es más compacta.
  • Fuerzas para dividir los bloques sincronizados para separar métodos.

Contras:

  • Sincroniza con esto y, por lo tanto, hace posible que personas externas también se sincronicen con él.
  • Es más difícil mover el código fuera del bloque sincronizado.

Bloque sincronizado

Pros:

  • Permite usar una variable privada para el bloqueo y forzarlo a permanecer dentro de la clase.
  • Los bloques sincronizados se pueden encontrar buscando referencias a la variable.

Contras:

  • La sintaxis es más complicada y hace que el código sea más difícil de leer.

Personalmente, prefiero usar métodos sincronizados con clases enfocadas solo a lo que necesita sincronización. Dicha clase debería ser lo más pequeña posible y, por lo tanto, debería ser fácil revisar la sincronización. Otros no deberían tener que preocuparse por la sincronización.

iny
fuente
Cuando dices "quédate dentro de la clase", ¿quieres decir "quédate dentro del objeto ", o me falta algo?
OldPeculier
36

La principal diferencia es que si usa un bloque sincronizado puede bloquear un objeto que no sea este, lo que le permite ser mucho más flexible.

Suponga que tiene una cola de mensajes y múltiples productores y consumidores de mensajes. No queremos que los productores interfieran entre sí, pero los consumidores deberían poder recuperar los mensajes sin tener que esperar a los productores. Entonces solo creamos un objeto

Object writeLock = new Object();

Y a partir de ahora, cada vez que un productor quiera agregar un nuevo mensaje, simplemente nos quedaremos con eso:

synchronized(writeLock){
  // do something
}

Por lo tanto, los consumidores aún pueden leer y los productores estarán bloqueados.

cdecker
fuente
2
Su ejemplo está limitado a lecturas no destructivas. Si la lectura elimina el mensaje de la cola, esto fallará si se hace en algún momento cuando un productor escribe en la cola.
ceving
30

Método sincronizado

Los métodos sincronizados tienen dos efectos.
Primero, cuando un hilo está ejecutando un método sincronizado para un objeto, todos los otros hilos que invocan métodos sincronizados para el mismo bloque de objeto (suspender la ejecución) hasta que el primer hilo se hace con el objeto.

En segundo lugar, cuando sale un método sincronizado, establece automáticamente una relación de suceso anterior con cualquier invocación posterior de un método sincronizado para el mismo objeto. Esto garantiza que los cambios en el estado del objeto sean visibles para todos los hilos.

Tenga en cuenta que los constructores no se pueden sincronizar: el uso de la palabra clave sincronizada con un constructor es un error de sintaxis. Sincronizar constructores no tiene sentido, porque solo el hilo que crea un objeto debe tener acceso a él mientras se está construyendo.

Declaración sincronizada

A diferencia de los métodos sincronizados, las declaraciones sincronizadas deben especificar el objeto que proporciona el bloqueo intrínseco: a menudo lo uso para sincronizar el acceso a una lista o mapa, pero no quiero bloquear el acceso a todos los métodos del objeto.

P: Bloqueos intrínsecos y sincronización La sincronización se basa en una entidad interna conocida como bloqueo intrínseco o bloqueo de monitor. (La especificación API a menudo se refiere a esta entidad simplemente como un "monitor".) Los bloqueos intrínsecos desempeñan un papel en ambos aspectos de la sincronización: imponen el acceso exclusivo al estado de un objeto y establecen relaciones previas que son esenciales para la visibilidad.

Cada objeto tiene un bloqueo intrínseco asociado. Por convención, un hilo que necesita acceso exclusivo y consistente a los campos de un objeto tiene que adquirir el bloqueo intrínseco del objeto antes de acceder a ellos, y luego liberar el bloqueo intrínseco cuando se hace con ellos. Se dice que un subproceso posee el bloqueo intrínseco entre el momento en que adquirió el bloqueo y lo liberó. Mientras un hilo posea un bloqueo intrínseco, ningún otro hilo puede adquirir el mismo bloqueo. El otro hilo se bloqueará cuando intente adquirir el bloqueo.

package test;

public class SynchTest implements Runnable {  
    private int c = 0;

    public static void main(String[] args) {
        new SynchTest().test();
    }

    public void test() {
        // Create the object with the run() method
        Runnable runnable = new SynchTest();
        Runnable runnable2 = new SynchTest();
        // Create the thread supplying it with the runnable object
        Thread thread = new Thread(runnable,"thread-1");
        Thread thread2 = new Thread(runnable,"thread-2");
//      Here the key point is passing same object, if you pass runnable2 for thread2,
//      then its not applicable for synchronization test and that wont give expected
//      output Synchronization method means "it is not possible for two invocations
//      of synchronized methods on the same object to interleave"

        // Start the thread
        thread.start();
        thread2.start();
    }

    public synchronized  void increment() {
        System.out.println("Begin thread " + Thread.currentThread().getName());
        System.out.println(this.hashCode() + "Value of C = " + c);
//      If we uncomment this for synchronized block, then the result would be different
//      synchronized(this) {
            for (int i = 0; i < 9999999; i++) {
                c += i;
            }
//      }
        System.out.println("End thread " + Thread.currentThread().getName());
    }

//    public synchronized void decrement() {
//        System.out.println("Decrement " + Thread.currentThread().getName());
//    }

    public int value() {
        return c;
    }

    @Override
    public void run() {
        this.increment();
    }
}

Verificación cruzada de diferentes salidas con método sincronizado, bloque y sin sincronización.

de repente
fuente
10
+1 por ser el único hasta ahora en mencionar que los constructores no pueden sincronizarse . Es decir, en un constructor realmente solo tiene una opción: bloques sincronizados.
ef2011
Probé su código según las instrucciones, pero C siempre es 0, luego -2024260031 y lo único que lo cambia es el código hash. ¿Qué comportamiento se debe ver?
Justin Johnson el
Debería haber citado a continuación los artículos de los que se ha proporcionado el contenido: docs.oracle.com/javase/tutorial/essential/concurrency/… y docs.oracle.com/javase/tutorial/essential/concurrency/…
Ravindra babu
29

Nota: los métodos y bloques estáticos sincronizados funcionan en el objeto Class.

public class MyClass {
   // locks MyClass.class
   public static synchronized void foo() {
// do something
   }

   // similar
   public static void foo() {
      synchronized(MyClass.class) {
// do something
      }
   }
}
Peter Lawrey
fuente
18

Cuando el compilador de Java convierte su código fuente en código de bytes, maneja métodos sincronizados y bloques sincronizados de manera muy diferente.

Cuando la JVM ejecuta un método sincronizado, el hilo de ejecución identifica que la estructura method_info del método tiene establecido el indicador ACC_SYNCHRONIZED, luego adquiere automáticamente el bloqueo del objeto, llama al método y libera el bloqueo. Si se produce una excepción, el subproceso libera automáticamente el bloqueo.

Sin embargo, la sincronización de un bloque de método omite el soporte incorporado de la JVM para adquirir el bloqueo de un objeto y el manejo de excepciones y requiere que la funcionalidad se escriba explícitamente en código de byte. Si lee el código de bytes de un método con un bloque sincronizado, verá más de una docena de operaciones adicionales para administrar esta funcionalidad.

Esto muestra llamadas para generar un método sincronizado y un bloque sincronizado:

public class SynchronizationExample {
    private int i;

    public synchronized int synchronizedMethodGet() {
        return i;
    }

    public int synchronizedBlockGet() {
        synchronized( this ) {
            return i;
        }
    }
}

El synchronizedMethodGet()método genera el siguiente código de bytes:

0:  aload_0
1:  getfield
2:  nop
3:  iconst_m1
4:  ireturn

Y aquí está el código de bytes del synchronizedBlockGet()método:

0:  aload_0
1:  dup
2:  astore_1
3:  monitorenter
4:  aload_0
5:  getfield
6:  nop
7:  iconst_m1
8:  aload_1
9:  monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow

Una diferencia significativa entre el método sincronizado y el bloque es que, el bloqueo sincronizado generalmente reduce el alcance del bloqueo. Como el alcance del bloqueo es inversamente proporcional al rendimiento, siempre es mejor bloquear solo la sección crítica del código. Uno de los mejores ejemplos de uso de bloque sincronizado es el bloqueo doblemente verificado en el patrón Singleton, donde en lugar de bloquear todogetInstance() método solo bloqueamos la sección crítica del código que se usa para crear la instancia Singleton. Esto mejora drásticamente el rendimiento porque el bloqueo solo se requiere una o dos veces.

Al utilizar métodos sincronizados, deberá tener especial cuidado si combina métodos sincronizados estáticos y no sincronizados.

Mohammad Adil
fuente
1
Si observamos el método sincronizado de bytecode, el bytecode es más compacto y simple, entonces, ¿por qué no es más rápido ese bloque sincronizado?
eatSleepCode
@eatSleepCode Tenga en cuenta que se trata de un código de bytes que JVM "compila". La JVM agregará lo necesariomonitorenter y monitorexitantes de ejecutar el código.
Philip Couling
12

La mayoría de las veces lo uso para sincronizar el acceso a una lista o mapa, pero no quiero bloquear el acceso a todos los métodos del objeto.

En el siguiente código, un subproceso que modifica la lista no bloqueará la espera de un subproceso que esté modificando el mapa. Si los métodos estuvieran sincronizados en el objeto, entonces cada método tendría que esperar aunque las modificaciones que están haciendo no entren en conflicto.

private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();

public void put( String s, Bar b ) {
  synchronized( myMap ) {
    myMap.put( s,b );
    // then some thing that may take a while like a database access or RPC or notifying listeners
  }
}

public void hasKey( String s, ) {
  synchronized( myMap ) {
    myMap.hasKey( s );
  }
}

public void add( Foo f ) {
  synchronized( myList ) {
    myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
  }
}

public Thing getMedianFoo() {
  Foo med = null;
  synchronized( myList ) {
    Collections.sort(myList);
    med = myList.get(myList.size()/2); 
  }
  return med;
}
Clint
fuente
7

Con los bloques sincronizados, puede tener múltiples sincronizadores, de modo que puedan ocurrir varias cosas simultáneas pero no conflictivas al mismo tiempo.

Paul Tomblin
fuente
6

Los métodos sincronizados se pueden verificar utilizando la API de reflexión. Esto puede ser útil para probar algunos contratos, como todos los métodos en el modelo están sincronizados .

El siguiente fragmento imprime todos los métodos sincronizados de Hashtable:

for (Method m : Hashtable.class.getMethods()) {
        if (Modifier.isSynchronized(m.getModifiers())) {
            System.out.println(m);
        }
}
Kojotak
fuente
5

Nota importante sobre el uso del bloque sincronizado: ¡cuidado con lo que usa como objeto de bloqueo!

El fragmento de código del usuario 2277816 anterior ilustra este punto en que se usa una referencia a un literal de cadena como objeto de bloqueo. Tenga en cuenta que los literales de cadena se internan automáticamente en Java y debe comenzar a ver el problema: ¡cada pieza de código que se sincroniza en el "bloqueo" literal comparte el mismo bloqueo! Esto puede conducir fácilmente a puntos muertos con piezas de código completamente no relacionadas.

No es solo con los objetos String con los que debes tener cuidado. Las primitivas en caja también son un peligro, ya que los métodos de autoboxing y valueOf pueden reutilizar los mismos objetos, dependiendo del valor.

Para obtener más información, consulte: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+reused

Søren Boisen
fuente
5

A menudo, usar un candado en un nivel de método es demasiado grosero. ¿Por qué bloquear un fragmento de código que no accede a ningún recurso compartido al bloquear un método completo? Dado que cada objeto tiene un bloqueo, puede crear objetos ficticios para implementar la sincronización a nivel de bloque. El nivel de bloque es más eficiente porque no bloquea todo el método.

Aquí un ejemplo

Nivel de método

class MethodLevel {

  //shared among threads
SharedResource x, y ;

public void synchronized method1() {
   //multiple threads can't access
}
public void synchronized method2() {
  //multiple threads can't access
}

 public void method3() {
  //not synchronized
  //multiple threads can access
 }
}

Nivel de bloque

class BlockLevel {
  //shared among threads
  SharedResource x, y ;

  //dummy objects for locking
  Object xLock = new Object();
  Object yLock = new Object();

    public void method1() {
     synchronized(xLock){
    //access x here. thread safe
    }

    //do something here but don't use SharedResource x, y
    // because will not be thread-safe
     synchronized(xLock) {
       synchronized(yLock) {
      //access x,y here. thread safe
      }
     }

     //do something here but don't use SharedResource x, y
     //because will not be thread-safe
    }//end of method1
 }

[Editar]

Para me Collectiongusta Vectory Hashtablese sincronizan cuando ArrayListo HashMapno, y necesita establecer una palabra clave sincronizada o invocar el método sincronizado de Colecciones:

Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
Maxim Shoustin
fuente
5

La única diferencia: los bloques sincronizados permiten el bloqueo granular a diferencia del método sincronizado

Básicamente synchronized, se han utilizado bloques o métodos para escribir código seguro para subprocesos evitando errores de inconsistencia de memoria.

Esta pregunta es muy antigua y muchas cosas han cambiado durante los últimos 7 años. Se han introducido nuevas construcciones de programación para la seguridad del hilo.

Puede lograr la seguridad de subprocesos mediante el uso de API de concurrencia avanzada en lugar de synchroniedbloques. Esta página de documentación proporciona buenas construcciones de programación para lograr la seguridad del subproceso.

Los objetos de bloqueo admiten modismos de bloqueo que simplifican muchas aplicaciones concurrentes.

Los ejecutores definen una API de alto nivel para lanzar y administrar hilos. Las implementaciones de ejecutor proporcionadas por java.util.concurrent proporcionan una gestión de grupo de subprocesos adecuada para aplicaciones a gran escala.

Las Colecciones concurrentes facilitan la gestión de grandes colecciones de datos y pueden reducir en gran medida la necesidad de sincronización.

Las variables atómicas tienen características que minimizan la sincronización y ayudan a evitar errores de consistencia de la memoria.

ThreadLocalRandom (en JDK 7) proporciona una generación eficiente de números pseudoaleatorios a partir de múltiples hilos.

El mejor reemplazo para sincronizado es ReentrantLock , que usa LockAPI

Un bloqueo reentrante de exclusión mutua con el mismo comportamiento básico y semántica que el bloqueo implícito del monitor al que se accede mediante métodos y declaraciones sincronizados, pero con capacidades extendidas.

Ejemplo con cerraduras:

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

Consulte los paquetes java.util.concurrent y java.util.concurrent.atomic también para otras construcciones de programación.

Consulte también esta pregunta relacionada:

Sincronización vs bloqueo

Ravindra babu
fuente
4

El método sincronizado se usa para bloquear todos los objetos El bloque sincronizado se usa para bloquear objetos específicos

kishore
fuente
3

En general, estos son en su mayoría los mismos, aparte de ser explícitos sobre el monitor del objeto que se está utilizando frente al implícito de este objeto. Una desventaja de los métodos sincronizados que creo que a veces se pasa por alto es que al usar la referencia "this" para sincronizar, está dejando abierta la posibilidad de que objetos externos se bloqueen en el mismo objeto. Eso puede ser un error muy sutil si te encuentras con él. La sincronización en un Objeto interno explícito u otro campo existente puede evitar este problema, encapsulando completamente la sincronización.

Alex Miller
fuente
2

Como ya se dijo aquí, el bloque sincronizado puede usar una variable definida por el usuario como objeto de bloqueo, cuando la función sincronizada usa solo "esto". Y, por supuesto, puede manipular las áreas de su función que deben sincronizarse. Pero todos dicen que no hay diferencia entre la función sincronizada y el bloque que cubre la función completa usando "esto" como objeto de bloqueo. Eso no es cierto, la diferencia está en el código de bytes que se generará en ambas situaciones. En caso de uso de bloque sincronizado se debe asignar una variable local que contenga referencia a "esto". Y como resultado, tendremos un tamaño un poco más grande para la función (no es relevante si solo tiene un número reducido de funciones).

Explicación más detallada de la diferencia que puede encontrar aquí: http://www.artima.com/insidejvm/ed2/threadsynchP.html

romano
fuente
2

En caso de métodos sincronizados, el bloqueo se adquirirá en un Objeto. Pero si utiliza el bloque sincronizado, tiene la opción de especificar un objeto en el que se adquirirá el bloqueo.

Ejemplo:

    Class Example {
    String test = "abc";
    // lock will be acquired on String  test object.
    synchronized (test) {
        // do something
    }

   lock will be acquired on Example Object
   public synchronized void testMethod() {
     // do some thing
   } 

   }
Srinu Yarru
fuente
2

Sé que esta es una vieja pregunta, pero con mi lectura rápida de las respuestas aquí, realmente no vi a nadie mencionar que a veces un synchronizedmétodo puede ser el bloqueo incorrecto .
De la concurrencia de Java en la práctica (pág. 72):

public class ListHelper<E> {
  public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...

public syncrhonized boolean putIfAbsent(E x) {
 boolean absent = !list.contains(x);
if(absent) {
 list.add(x);
}
return absent;
}

El código anterior tiene la apariencia de ser seguro para subprocesos. Sin embargo, en realidad no lo es. En este caso, el bloqueo se obtiene en la instancia de la clase. Sin embargo, es posible que la lista sea ​​modificada por otro hilo que no use ese método. El enfoque correcto sería usar

public boolean putIfAbsent(E x) {
 synchronized(list) {
  boolean absent = !list.contains(x);
  if(absent) {
    list.add(x);
  }
  return absent;
}
}

El código anterior bloquearía todos los hilos que intentan modificar la lista para que no modifique la lista hasta que se complete el bloqueo sincronizado.

aarbor
fuente
leyendo este libro en este momento ... me pregunto ... si esa lista era privada en lugar de pública y solo tenía el método putIfAbsent, sincronizado (esto) sería suficiente ¿verdad? El problema en cuestión es porque la lista también se puede modificar fuera de ese ListHelper.
dtc
@dtc sí, si la lista era privada y no se filtró en ningún otro lugar de la clase, eso sería suficiente, siempre que haya marcado cualquier otro método en la clase que modifique la lista como sincronizada también. Sin embargo, el bloqueo de todo el método en lugar de solo el Listpuede conducir a problemas de rendimiento si hay un registro de código que no necesariamente necesita sincronizarse
aarbor
eso tiene sentido. ¡Muchas gracias por responder! tbh, he encontrado que el libro es bastante útil para ampliar mi conocimiento y cómo abordar el subprocesamiento múltiple, pero también me introdujo un mundo completamente nuevo de confusión
2017
2

Como cuestión práctica, la ventaja de los métodos sincronizados sobre los bloques sincronizados es que son más resistentes a los idiotas; debido a que no puede elegir un objeto arbitrario para bloquear, no puede usar incorrectamente la sintaxis del método sincronizado para hacer cosas estúpidas como bloquear un literal de cadena o bloquear el contenido de un campo mutable que se cambia desde debajo de los hilos.

Por otro lado, con los métodos sincronizados no puede proteger el bloqueo de cualquier subproceso que pueda obtener una referencia al objeto.

Por lo tanto, usar sincronizado como un modificador en los métodos es mejor para proteger a sus orcos de lastimarse a sí mismos, mientras que usar bloques sincronizados junto con objetos privados de bloqueo final es mejor para proteger su propio código de los orquineros.

Nathan Hughes
fuente
1

De un resumen de especificaciones de Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html

La declaración sincronizada (§14.17) calcula una referencia a un objeto; luego intenta realizar una acción de bloqueo en ese objeto y no continúa hasta que la acción de bloqueo se haya completado con éxito. ...

Un método sincronizado (§8.4.3.5) realiza automáticamente una acción de bloqueo cuando se invoca; su cuerpo no se ejecuta hasta que la acción de bloqueo se haya completado con éxito. Si el método es un método de instancia , bloquea el bloqueo asociado con la instancia para la que se invocó (es decir, el objeto que se conocerá como este durante la ejecución del cuerpo del método). Si el método es estático , bloquea el bloqueo asociado con el objeto Clase que representa la clase en la que se define el método. ...

En base a estas descripciones, diría que la mayoría de las respuestas anteriores son correctas, y un método sincronizado podría ser particularmente útil para métodos estáticos, donde de lo contrario tendría que descubrir cómo obtener el "Objeto de clase que representa la clase en la que se utilizó el método definido ".

Editar: originalmente pensé que estas eran citas de la especificación real de Java. Aclaró que esta página es solo un resumen / explicación de la especificación

Josiah Yoder
fuente
1

TLDR; No utilice el synchronizedmodificador ni la synchronized(this){...}expresión, sino synchronized(myLock){...}dónde myLockes un campo de instancia final que contiene un objeto privado.


La diferencia entre usar el synchronizedmodificador en la declaración del método y la synchronized(..){ }expresión en el cuerpo del método es la siguiente:

  • El synchronizedmodificador especificado en la firma del método.
    1. es visible en el JavaDoc generado,
    2. es determinable mediante programación mediante reflexión cuando se prueba un modificador de método para Modificador . SINCRONIZADO ,
    3. requiere menos tipeo e sangría en comparación con synchronized(this) { .... }, y
    4. (dependiendo de su IDE) es visible en el esquema de la clase y la finalización del código,
    5. usa el thisobjeto como bloqueo cuando se declara en un método no estático o la clase adjunta cuando se declara en un método estático.
  • La synchronized(...){...}expresión te permite
    1. para sincronizar solo la ejecución de partes del cuerpo de un método,
    2. para ser utilizado dentro de un constructor o un ( estático bloque de inicialización ),
    3. para elegir el objeto de bloqueo que controla el acceso sincronizado.

Sin embargo, usar el synchronizedmodificador o synchronized(...) {...}con thisel objeto de bloqueo (como en synchronized(this) {...}) tiene la misma desventaja. Ambos usan su propia instancia como el objeto de bloqueo para sincronizar. Esto es peligroso porque no solo el objeto en sí, sino cualquier otro objeto / código externo que tenga una referencia a ese objeto también puede usarlo como un bloqueo de sincronización con efectos secundarios potencialmente graves (degradación del rendimiento y puntos muertos ).

Por lo tanto, la mejor práctica es no usar el synchronizedmodificador ni la synchronized(...)expresión junto con thisun objeto de bloqueo, sino un objeto de bloqueo privado para este objeto. Por ejemplo:

public class MyService {
    private final lock = new Object();

    public void doThis() {
       synchronized(lock) {
          // do code that requires synchronous execution
        }
    }

    public void doThat() {
       synchronized(lock) {
          // do code that requires synchronous execution
        }
    }
}

También puede usar varios objetos de bloqueo, pero se debe tener especial cuidado para garantizar que esto no provoque bloqueos cuando se usa anidado.

public class MyService {
    private final lock1 = new Object();
    private final lock2 = new Object();

    public void doThis() {
       synchronized(lock1) {
          synchronized(lock2) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThat() and doMore().
          }
    }

    public void doThat() {
       synchronized(lock1) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThis().
              // doMore() may execute concurrently
        }
    }

    public void doMore() {
       synchronized(lock2) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThis().
              // doThat() may execute concurrently
        }
    }
}
Sebastian Thomschke
fuente
1

Supongo que esta pregunta es sobre la diferencia entre Thread Safe Singleton y Lazy initialization with Double check lock . Siempre me refiero a este artículo cuando necesito implementar algún singleton específico.

Bueno, este es un Thread Safe Singleton :

// Java program to create Thread Safe 
// Singleton class 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

 //synchronized method to control simultaneous access 
  synchronized public static GFG getInstance()  
  { 
    if (instance == null)  
    { 
      // if instance is null, initialize 
      instance = new GFG(); 
    } 
    return instance; 
  } 
} 

Pros:

  1. La inicialización perezosa es posible.

  2. Es seguro para hilos.

Contras:

  1. El método getInstance () está sincronizado, por lo que causa un rendimiento lento, ya que varios subprocesos no pueden acceder a él simultáneamente.

Esta es una inicialización diferida con bloqueo de doble verificación :

// Java code to explain double check locking 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

  public static GFG getInstance() 
  { 
    if (instance == null)  
    { 
      //synchronized block to remove overhead 
      synchronized (GFG.class) 
      { 
        if(instance==null) 
        { 
          // if instance is null, initialize 
          instance = new GFG(); 
        } 

      } 
    } 
    return instance; 
  } 
} 

Pros:

  1. La inicialización perezosa es posible.

  2. También es seguro para hilos.

  3. El rendimiento se reduce debido a que se supera la palabra clave sincronizada.

Contras:

  1. Primera vez, puede afectar el rendimiento.

  2. Como contras. El método de bloqueo de doble verificación es soportable, por lo que puede usarse para aplicaciones de subprocesos múltiples de alto rendimiento.

Consulte este artículo para obtener más detalles:

https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/

de indefinido
fuente
-3

Sincronización con hilos. 1) NUNCA use sincronizado (esto) en un hilo que no funciona. La sincronización con (esto) usa el hilo actual como el objeto de hilo de bloqueo. Dado que cada hilo es independiente de otros hilos, NO hay coordinación de sincronización. 2) Las pruebas de código muestran que en Java 1.6 en una Mac, la sincronización del método no funciona. 3) sincronizado (lockObj) donde lockObj es un objeto compartido común de todos los hilos que se sincronizarán en él. 4) ReenterantLock.lock () y .unlock () funcionan. Vea los tutoriales de Java para esto.

El siguiente código muestra estos puntos. También contiene el Vector seguro para subprocesos que sería sustituido por ArrayList, para mostrar que muchos subprocesos que se agregan a un Vector no pierden ninguna información, mientras que lo mismo con un ArrayList puede perder información. 0) El código actual muestra la pérdida de información debido a las condiciones de carrera A) Comente la línea A etiquetada actual, y elimine el comentario de la línea A por encima de ella, luego ejecute, el método pierde datos pero no debería. B) Invierta el paso A, descomente B y // bloque final}. Luego ejecute para ver resultados sin pérdida de datos C) Comente B, descomente C. Ejecute, vea que la sincronización en (esto) pierde datos, como se esperaba. No tengo tiempo para completar todas las variaciones, espero que esto ayude. Si la sincronización está activada (esto), o la sincronización del método funciona, indique qué versión de Java y sistema operativo ha probado. Gracias.

import java.util.*;

/** RaceCondition - Shows that when multiple threads compete for resources 
     thread one may grab the resource expecting to update a particular 
     area but is removed from the CPU before finishing.  Thread one still 
     points to that resource.  Then thread two grabs that resource and 
     completes the update.  Then thread one gets to complete the update, 
     which over writes thread two's work.
     DEMO:  1) Run as is - see missing counts from race condition, Run severa times, values change  
            2) Uncomment "synchronized(countLock){ }" - see counts work
            Synchronized creates a lock on that block of code, no other threads can 
            execute code within a block that another thread has a lock.
        3) Comment ArrayList, unComment Vector - See no loss in collection
            Vectors work like ArrayList, but Vectors are "Thread Safe"
         May use this code as long as attribution to the author remains intact.
     /mf
*/ 

public class RaceCondition {
    private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
//  private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)

    private String countLock="lock";    // Object use for locking the raceCount
    private int raceCount = 0;        // simple add 1 to this counter
    private int MAX = 10000;        // Do this 10,000 times
    private int NUM_THREADS = 100;    // Create 100 threads

    public static void main(String [] args) {
    new RaceCondition();
    }

    public RaceCondition() {
    ArrayList<Thread> arT = new ArrayList<Thread>();

    // Create thread objects, add them to an array list
    for( int i=0; i<NUM_THREADS; i++){
        Thread rt = new RaceThread( ); // i );
        arT.add( rt );
    }

    // Start all object at once.
    for( Thread rt : arT ){
        rt.start();
    }

    // Wait for all threads to finish before we can print totals created by threads
    for( int i=0; i<NUM_THREADS; i++){
        try { arT.get(i).join(); }
        catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
    }

    // All threads finished, print the summary information.
    // (Try to print this informaiton without the join loop above)
    System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
                MAX*NUM_THREADS, raceList.size(), raceCount );
    System.out.printf("Array lost %,d. Count lost %,d\n",
             MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
    }   // end RaceCondition constructor



    class RaceThread extends Thread {
    public void run() {
        for ( int i=0; i<MAX; i++){
        try {
            update( i );        
        }    // These  catches show when one thread steps on another's values
        catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
        catch( OutOfMemoryError oome ) { System.out.print("O"); }
        }
    }

    // so we don't lose counts, need to synchronize on some object, not primitive
    // Created "countLock" to show how this can work.
    // Comment out the synchronized and ending {, see that we lose counts.

//    public synchronized void update(int i){   // use A
    public void update(int i){                  // remove this when adding A
//      synchronized(countLock){            // or B
//      synchronized(this){             // or C
        raceCount = raceCount + 1;
        raceList.add( i );      // use Vector  
//          }           // end block for B or C
    }   // end update

    }   // end RaceThread inner class


} // end RaceCondition outter class
usuario2277816
fuente
1
Sincronización con '(este)' hace el trabajo, y no no 'utilizar el hilo actual como el objeto de sincronización', a menos que el objeto actual es de una clase que se extiende Thread. -1
Marqués de Lorne