¿Fue útil?

Solución

Son equivalentes en función, aunque los compiladores que probé (Java 1.6.0_07 y Eclipse 3.4) generan un código de bytes diferente. El primero genera:

// access flags 33
public synchronized someMethod()V
  RETURN

El segundo genera:

// access flags 1
public someMethod()V
  ALOAD 0
  DUP
  MONITORENTER
  MONITOREXIT
  RETURN

(Gracias a ASM por la impresión del código de bytes).

Entonces, la diferencia entre ellos persiste en el nivel de código de bytes, y depende de la JVM hacer que su comportamiento sea el mismo. Sin embargo, tienen el mismo efecto funcional: consulte ejemplo en la Especificación del lenguaje Java.

Cabe señalar que si el método se anula en una subclase, no está necesariamente sincronizado, por lo que tampoco hay diferencia a ese respecto.

También ejecuté una prueba para bloquear un subproceso intentando acceder al monitor en cada caso para comparar cómo se verían sus rastros de pila en un volcado de subprocesos, y ambos contenían el método en cuestión, por lo que tampoco hay diferencia allí.

Otros consejos

Hice el comentario original de que las declaraciones son idénticas.

En ambos casos, lo primero que sucede es que el hilo de llamada intentará adquirir el monitor del objeto actual (es decir, this ').

No sé sobre diferentes bytecode, estaré feliz de escuchar la diferencia. Pero en la práctica, son 100% idénticos.

EDITAR: voy a aclarar esto ya que algunas personas aquí se equivocaron. Considere:

public class A {
    public synchronized void doStuff()
    {
        // do stuff
    }
}

public class B extends A {
    public void doStuff()
    {
        // do stuff
        // THIS IS OVERRIDE!
    }
}

En este caso, doStuff () en la clase B todavía anula doStuff () en la clase A aunque no esté sincronizado.

¡La palabra clave sincronizada nunca es parte del contrato! No para subclases, ni para interfaces, ni para clases abstractas.

Hice el comentario original. Mi comentario fue que son lógicamente equivalentes, pero compilan a diferentes bytecode .

No agregué nada más para justificarlo en ese momento porque no hay mucho para justificar realmente, simplemente hacen compilar a diferentes códigos de bytes. Si declara un método como sincronizado , esa sincronización es parte de la definición del método. Un bloque sincronizado dentro de un método no es parte de la definición del método , sino que implica bytecodes separados para adquirir y liberar el monitor, como lo ilustra uno de los carteles anteriores. Estrictamente hablando, son cosas ligeramente diferentes, aunque a la lógica general de su programa , son equivalentes .

¿Cuándo importa esto? Bueno, en la mayoría de las máquinas virtuales de escritorio modernas, casi nunca. Pero por ejemplo:

  • una máquina virtual podría en principio hacer optimizaciones en un caso pero no en el otro
  • hay algunas optimizaciones del compilador JIT donde el número de bytecodes en el método se toma como criterio para qué optimizaciones hacer
  • una VM sin un compilador JIT (ciertamente pocos hoy en día, pero ¿tal vez en un dispositivo móvil más antiguo?) tendrá más códigos de byte para procesar en cada llamada

Sí. El uso de la palabra clave sincronizada en un método de instancia usa 'this' como monitor, también lo usa en un método de clase (método estático) usa la clase 'Objeto de clase (Foo.class).

De esta forma, puede sincronizar métodos completos y, al mismo tiempo, sincronizarlo con un fragmento de código en otro método utilizando el estilo de bloque sincronizado.

No puedo ver ninguna diferencia funcional: ambos sincronizan sus cuerpos de métodos completos en (esto). ¿Cómo justificó su declaración la persona que comentó que son diferentes?

No son del todo equivalentes en función. Otro código podría usar la reflexión para ver si su método tiene el modificador sincronizado, pero no hay forma de saber si un método contiene un bloque sincronizado sin leer su código de bytes.

La capacidad de determinar si un método está sincronizado ocasionalmente es útil. Personalmente, he usado ese indicador para evitar bloqueos redundantes al sincronizar en programación orientada a aspectos.

MyObject myObjectA;
MyObject myObjectB;

public void someMethod() {
  synchronized (this) {
    //stuff
  }
}

public void someMethodA() {
  synchronized (myObjectA) {
    //stuff
  }
}

public void someMethodB() {
  synchronized (myObjectB) {
    //stuff
  }
}

En este caso:

  • someMethod bloquea toda la clase
  • someMethodA bloquea myObjectA solamente
  • someMethodB bloquea myObjectB solamente
  • someMethodA y someMethodB se pueden invocar al mismo tiempo
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top