Kann mir jemand den Vorteil einer synchronisierten Methode gegenüber einem synchronisierten Block anhand eines Beispiels erläutern?
Antworten
Zu viele Anzeigen?Wenn der Java-Compiler Ihren Quellcode in Bytecode umwandelt, behandelt er synchronisierte Methoden und synchronisierte Blöcke sehr unterschiedlich.
Wenn die JVM eine synchronisierte Methode ausführt, erkennt der ausführende Thread, dass in der method_info-Struktur der Methode das ACC_SYNCHRONIZED-Flag gesetzt ist, erwirbt dann automatisch die Sperre des Objekts, ruft die Methode auf und gibt die Sperre frei. Wenn eine Ausnahme auftritt, gibt der Thread die Sperre automatisch wieder frei.
Die Synchronisierung eines Methodenblocks umgeht dagegen die in der JVM eingebaute Unterstützung für den Erwerb der Sperre eines Objekts und die Behandlung von Ausnahmen und erfordert, dass die Funktionalität explizit in Bytecode geschrieben wird. Wenn Sie den Bytecode für eine Methode mit einem synchronisierten Block lesen, werden Sie mehr als ein Dutzend zusätzlicher Operationen zur Verwaltung dieser Funktionalität sehen.
Hier werden Aufrufe zur Erzeugung einer synchronisierten Methode und eines synchronisierten Blocks gezeigt:
public class SynchronizationExample {
private int i;
public synchronized int synchronizedMethodGet() {
return i;
}
public int synchronizedBlockGet() {
synchronized( this ) {
return i;
}
}
}
Le site synchronizedMethodGet()
Methode erzeugt den folgenden Bytecode:
0: aload_0
1: getfield
2: nop
3: iconst_m1
4: ireturn
Und hier ist der Bytecode aus der synchronizedBlockGet()
Methode:
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
Ein signifikanter Unterschied zwischen einer synchronisierten Methode und einem Block besteht darin, dass ein synchronisierter Block im Allgemeinen den Umfang der Sperre reduziert. Da der Umfang der Sperre umgekehrt proportional zur Leistung ist, ist es immer besser, nur kritische Abschnitte des Codes zu sperren. Eines der besten Beispiele für die Verwendung eines synchronisierten Blocks ist doppelt geprüftes Sperren im Singleton-Muster wo statt der Sperrung ganzer getInstance()
Methode sperren wir nur den kritischen Abschnitt des Codes, der zur Erstellung der Singleton-Instanz verwendet wird. Dies verbessert die Leistung drastisch, da das Sperren nur ein oder zwei Mal erforderlich ist.
Bei der Verwendung von synchronisierten Methoden müssen Sie besonders vorsichtig sein, wenn Sie sowohl statische synchronisierte als auch nicht-statische synchronisierte Methoden mischen.
Meistens verwende ich dies, um den Zugriff auf eine Liste oder Karte zu synchronisieren, aber ich möchte nicht den Zugriff auf alle Methoden des Objekts blockieren.
Im folgenden Code wird ein Thread, der die Liste ändert, nicht blockiert, wenn er auf einen Thread wartet, der die Karte ändert. Wären die Methoden auf dem Objekt synchronisiert, müsste jede Methode warten, auch wenn die Änderungen, die sie vornehmen, nicht in Konflikt stehen würden.
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;
}
Synchronisierte Methoden können mit der Reflection-API überprüft werden. Dies kann für das Testen einiger Verträge nützlich sein, wie z.B. alle Methoden im Modell sind synchronisiert .
Das folgende Snippet gibt alle synchronisierten Methoden von Hashtable aus:
for (Method m : Hashtable.class.getMethods()) {
if (Modifier.isSynchronized(m.getModifiers())) {
System.out.println(m);
}
}