Frage

Während der Diskussion einer Java -Synchronisation Frage, Jemand hat einen Kommentar abgegeben, dass die folgenden Ausschnitte nicht gleichwertig sind (und möglicherweise zu verschiedenen Bytecodes kompiliert werden):

public synchronized void someMethod() {
  //stuff
}

und

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

Sind sie gleichwertig?

War es hilfreich?

Lösung

Sie sind gleichwertig in der Funktion, obwohl die von mir getesteten Compiler (Java 1.6.0_07 und Eclipse 3.4) unterschiedliche Bytecode generieren. Der erste erzeugt:

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

Die zweite erzeugt:

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

(Dank an Asm für den Bytecode -Druck).

Der Unterschied zwischen ihnen bleibt also auf der Bytecode -Ebene bestehen, und es liegt an der JVM, ihr Verhalten gleich zu gestalten. Sie haben jedoch den gleichen funktionalen Effekt - siehe die Beispiel In der Java -Sprachspezifikation.

Es sollte beachtet werden, dass, wenn die Methode in einer Unterklasse überschrieben wird, dass sie nicht unbedingt synchronisiert ist - so gibt es auch keinen Unterschied in diesem Hinsicht.

Ich habe auch einen Test durchgeführt, um einen Thread zu blockieren, der in jedem Fall auf den Monitor zugreift, um zu vergleichen, wie ihre Stapelspuren in einem Thread -Dump aussehen würden, und beide enthielten die betreffende Methode, so dass es dort auch keinen Unterschied gibt.

Andere Tipps

Ich habe den ursprünglichen Kommentar abgegeben, dass die Aussagen identisch sind.

In beiden Fällen wird als erstes statt this') Monitor.

Ich weiß nichts über verschiedene Bytecode, ich werde mich freuen, den Unterschied zu hören. Aber in der Praxis sind sie zu 100% identisch.

BEARBEITEN: Ich werde dies klarstellen, da einige Leute hier falsch verstanden haben. In Betracht ziehen:

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

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

In diesem Fall doStuff() in Klasse B überschreibt immer noch doStuff() in Klasse A, obwohl es nicht synchronisiert ist.

Synchronisiertes Schlüsselwort ist nie Teil des Vertrags! Nicht für Unterklassen, nicht für Schnittstellen, nicht für abstrakte Klassen.

Ich habe den ursprünglichen Kommentar gemacht. Mein Kommentar war, dass sie logisch äquivalent sind, aber Kompilieren Sie zu verschiedenen Bytecode.

Ich habe noch nichts hinzugefügt, um es zu der Zeit zu rechtfertigen, weil es nicht viel zu rechtfertigen gibt tun Kompilieren Sie zu verschiedenen Bytecode. Wenn Sie eine Methode deklarieren als synchronisiert, Dann ist diese Synchronisation Teil der Definition der Methode. Ein synchronisierter Block innerhalb einer Methode nicht Teil der Definition der Methode, aber stattdessen beinhaltet separate Bytecodes, um den Monitor zu erwerben und freizusetzen, wie eines der oben genannten Poster dargestellt hat. Streng genommen sind sie jedoch etwas andere Dinge zum Gesamtlogik Ihres Programms, Sie sind gleichwertig.

Wann ist das wichtig? Nun, bei den meisten modernen Desktop -VMs kaum jemals. Aber zum Beispiel:

  • Ein VM könnte im Prinzip in einem Fall optimiert werden, aber nicht in der anderen
  • Es gibt einige JIT -Compiler -Optimierungen, bei denen die Die Anzahl der Bytecodes in der Methode wird als Kriterium für die Optimierungen angesehen
  • eine VM ohne Ein JIT -Compiler (zugegebenermaßen heutzutage nur wenige, aber vielleicht auf einem älteren mobilen Gerät) hat mehr Bytecodes für jeden Anruf zu verarbeiten

Ja. Verwendet das synchronisierte Schlüsselwort für eine Instanzmethode 'this' als Monitor. Verwenden Sie es auch in einer Klassenmethode (statische Methode) das Klasse -Klassenobjekt (FOO.CLASS).

Auf diese Weise können Sie ganze Methoden synchronisieren und gleichzeitig mit einem Code-Snippet in einer anderen Methode mit dem Synchron-Block-Stil synchronisieren.

Ich kann keinen funktionalen Unterschied sehen - beide synchronisieren ihre gesamten Methodenkörper (dies). Wie hat die Person, die kommentiert hat, dass dies unterschiedlich ist, ihre Aussage rechtfertigt?

Sie sind in der Funktion nicht ganz äquivalent. Ein anderer Code könnte über Reflexion erfahren, ob Ihre Methode den synchronisierten Modifikator hat. Es gibt jedoch keine Möglichkeit zu erkennen, ob eine Methode einen synchronisierten Block enthält, ohne den Bytecode zu lesen.

Die Fähigkeit zu bestimmen, ob eine Methode gelegentlich synchronisiert ist, ist nützlich. Persönlich habe ich dieses Flag verwendet, um eine redundante Verriegelung bei der Synchronisation in der Aspekt-orientierten Programmierung zu vermeiden.

MyObject myObjectA;
MyObject myObjectB;

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

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

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

In diesem Fall:

  • someMethod Blockiert die ganze Klasse
  • someMethodA Blöcke myObjectA nur
  • someMethodB Blöcke myObjectB nur
  • someMethodA und someMethodB kann gleichzeitig aufgerufen werden
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top