4.15. JVM-Befehlssatz: Kurzbeschreibung M
Betrete den Monitor für ein bestimmtes Objekt (Synchronisation nebenläufiger
Prozesse).
Um nebenläufige Prozesse synchronisieren zu können wird in Java das Konzept
des Monitors verwendet. Innerhalb eines Java-Quelltextes kann
die synchronized
-Anweisung verwendet verwendet werden, um einen Block
von Anweisungen für einen anderen Thread zu sperren. Die Kapselung eines Blocks
zur Synchronisation von Threads und die automatische Verwaltung einer zum Block
gehörenden Sperre wird als Monitor bezeichnet. Ein Thread kann also einen Monitor
sperren (lock) und wieder freigeben (unlock) und nur ein Thread kann die Sperre
für einen Monitor besitzen.
Der Compiler erzeugt für eine synchronized
-Anweisung die
JVM-Befehle monitorenter
und
monitorexit
,
die das Betreten und Verlassen des Monitors realisieren. Jedem Objekt kann ein
Monitor zugeordnet werden. Der Monitor ergibt sich aus der Objektreferenz
objectref, die sich vor der Ausführung von
monitorenter
auf dem Operandenstapel befindet. Ein Thread, der
monitorenter
ausführt, beansprucht den Monitor. Falls der Monitor
durch einen anderen Thread bearbeitet wird, wartet der ursprüngliche Thread bis
der Monitor durch monitorexit
wieder freigegeben wurde. Ein Monitor
besitzt einen Zähler, der für jeden Thread aufzeichnet, wie oft dieser den Monitor
betreten hat.
Beispiel:
Das folgende Beispielprogramm enthält die statische Variable a
,
die durch zwei Threads erhöht werden kann. Die beiden Threads werden innerhalb der
main
-Methode gestartet und führen danach die Anweisungen der
run
-Methode aus. Das Hochzählen der Klassenvariablen a
und die Ausgabe ihres Wertes durch die beiden Threads erfolgt asynchron, wenn
die beiden Anweisungen innerhalb der synchronized
-Blocks nicht
gekapselt wären. Ohne Verwendung von synchronized
kann der Fall
eintreten, dass der eine Thread die Zählvariable um eins erhöht und vor der
Kosolenausgabe des Wertes durch einen Scheduler (Steuerungsprogramm) in Wartestellung
gesetzt wird. Der zweite Thread wird nun fortgeführt, erhöht a
um eins
und gibt den Zahlenwert aus. Die Zahlenwerte in der Konsolenausgabe können also
Lücken aufweisen (z.B. ..., 377, 378, 380, ..., 655, 379,
656, ...). Um dies zu verhindern wird eine synchronized
-Anweisung
verwendet. Dazu wird eine Objektreferenz (objectref)
benötigt, die durch die Methode getClass
festgelegt wird (ein
Monitor wird mit einem Objekt assoziiert). Mit Hilfe dieser Methode kann das
sogenannte Klassenobjekt clazz
erhalten werden,
das die Laufzeitklasse für dieses Objekt repräsentiert.
public class Test extends Thread {
static int a = 0;
public void run() { public void run();
Code:
Class clazz = this.getClass(); 0: aload_0
1: invokevirtual #2; //Method java/lang/
Object.getClass:()Ljava/lang/Class;
4: astore_1
while (a < 1000) { 5: getstatic #3; //Field a:I
8: sipush 1000
11: if_icmpge 48
synchronized (clazz) { 14: aload_1
15: dup
16: astore_2
17: monitorenter
a++; 18: getstatic #3; //Field a:I
21: iconst_1
22: iadd
23: putstatic #3; //Field a:I
System.out.println(a); 26: getstatic #4; //Field java/lang/
System.out:Ljava/io/PrintStream;
29: getstatic #3; //Field a:I
32: invokevirtual #5; //Method java/io/
PrintStream.println:(I)V
35: aload_2
} 36: monitorexit
37: goto 45
40: astore_3
41: aload_2
42: monitorexit
43: aload_3
44: athrow
} 45: goto 5
} 48: return
Exception table:
from to target type
18 37 40 any
40 43 40 any
public static void
main(String[] args) {
Thread t1 = new Test();
Thread t2 = new Test();
t1.start();
t2.start();
}
}
Die Anweisung a++
und die Anweisung für die Konsolenausgabe
werden auf Bytecodeebene durch die beiden JVM-Befehle monitorenter
und monitorexit
eingeschlossen. Innerhalb der JVM-Befehlsfolge
tritt eine zweite monitorexit
-Anweisung bei Bytecodestelle 42 auf.
Diese wird verwendet, um einen gesperrten Monitor, auch beim Auftreten einer
Ausnahme (Exception), geordnet verlassen zu können. Beim Auftreten einer
Ausnahme wird bei Stelle 40 im Bytecode fortgefahren (siehe Exception-Tabelle)
und mit athrow
wird die Ausnahme an den Aufrufer der zugehörigen
Methode weitergegeben.
Verlasse den Monitor für ein bestimmtes Objekt (Synchronisation nebenläufiger
Prozesse).
Siehe dazu die Beschreibung und das Beispiel von
monitorenter
.
Erzeuge ein neues mehrdimensionales Array.
Mit Hilfe der vorzeichenlosen Bytes indexbyte1 und
indexbyte2 wird ein Index im (Laufzeit-)Konstantenpool der
aktuellen Klasse berechnet: (indexbyte1 << 8) |
indexbyte2. Durch die Konstante bei diesem Index kann der
Felddeskriptor erhalten werden, der den Typ des mehrdimensionalen Arrays beschreibt
(z.B. [[I
oder [[Ljava/lang/Integer;
für ein
mehrdimensionales int
- bzw. Integer
-Array).
Beispiel:
int[][] i1 = new int[2][2]; 0: iconst_2
1: iconst_2
2: multianewarray #2, 2; //class "[[I"
6: astore_1
i1[0][1] = 3; 7: aload_1
8: iconst_0
9: aaload
10: iconst_1
11: iconst_3
12: iastore
Integer[][] i2 = new Integer[4][4]; 13: iconst_4
14: iconst_4
15: multianewarray #3, 2;
//class "[[Ljava/lang/Integer;"
19: astore_2
i2[2][3] = new Integer(4); 20: aload_2
21: iconst_2
22: aaload
23: iconst_3
24: new #4; //class java/lang/Integer
27: dup
28: iconst_4
29: invokespecial #5; //Method java/lang/
Integer."<init>":(I)V
32: aastore