Galileo Computing < openbook >
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.
Galileo Computing - Professionelle Buecher. Auch fuer Einsteiger.


Java ist auch eine Insel von Christian Ullenboom
Buch: Java ist auch eine Insel (Galileo Computing)
gp Kapitel 9 Threads und nebenläufige Programmierung
gp 9.1 Prozesse und Threads
gp 9.1.1 Wie parallele Programme die Geschwindigkeit steigern können
gp 9.2 Threads erzeugen
gp 9.2.1 Threads über die Schnittstelle Runnable implementieren
gp 9.2.2 Threads über Runnable starten
gp 9.2.3 Die Klasse Thread erweitern
gp 9.2.4 Erweitern von Thread oder Implementieren von Runnable?
gp 9.3 Threads schlafen
gp 9.3.1 Eine Zeituhr
gp 9.4 Die Klassen Timer und TimerTask
gp 9.5 Die Zustände eines Threads
gp 9.5.1 Das Ende eines Threads
gp 9.5.2 Einen Thread höflich mit Interrupt beenden
gp 9.5.3 Der stop() von außen
gp 9.5.4 Das ThreadDeath-Objekt
gp 9.5.5 Auf das Ende warten mit join()
gp 9.6 Arbeit niederlegen und wieder aufnehmen
gp 9.7 Priorität
gp 9.7.1 Threads hoher Priorität und das AWT
gp 9.7.2 Granularität und Vorrang
gp 9.8 Dämonen
gp 9.9 Kooperative und nichtkooperative Threads
gp 9.10 Synchronisation über kritische Abschnitte
gp 9.10.1 Gemeinsam genutzte Daten
gp 9.10.2 Probleme beim gemeinsamen Zugriff und kritische Abschnitte
gp 9.10.3 Punkte parallel initialisieren
gp 9.10.4 i++ sieht atomar aus, ist es aber nicht
gp 9.10.5 Abschnitte mit synchronized schützen
gp 9.10.6 Monitore
gp 9.10.7 Synchronized-Methode am Beispiel der Klasse StringBuffer
gp 9.10.8 Synchronisierte Blöcke
gp 9.10.9 Vor- und Nachteile von synchronisierten Blöcken und Methoden
gp 9.10.10 Nachträglich synchronisieren
gp 9.10.11 Monitore sind reentrant, gut für die Geschwindigkeit
gp 9.10.12 Deadlocks
gp 9.10.13 Erkennen von Deadlocks
gp 9.11 Variablen mit volatile kennzeichnen
gp 9.12 Synchronisation über Warten und Benachrichtigen
gp 9.12.1 Falls der Lock fehlt: IllegalMonitorStateException
gp 9.12.2 Warten mit wait() und Aufwecken mit notify()
gp 9.12.3 Mehrere Wartende und notifyAll()
gp 9.12.4 wait() mit einer Zeitspanne
gp 9.12.5 Beispiel Erzeuger-Verbraucher-Programm
gp 9.12.6 Semaphoren
gp 9.12.7 Die Concurrency Utilities von Doug Lea
gp 9.13 Aktive Threads in der Umgebung
gp 9.14 Gruppen von Threads in einer Thread-Gruppe
gp 9.14.1 Etwas über die aktuelle Thread-Gruppe herausfinden
gp 9.14.2 Threads in einer Thread-Gruppe anlegen
gp 9.14.3 Methoden von Thread und ThreadGroup im Vergleich
gp 9.15 Einen Abbruch der virtuellen Maschine erkennen


Galileo Computing

9.11 Variablen mit volatile kennzeichnentoptop

Die JVM arbeitet bei den Ganzzahl-Datentypen kleiner gleich int intern nur mit einem int. Doch obwohl es auch für die Datentypen, die größer als 32 Bit sind, also long und double, einzelne Bytecode-Befehle gibt, ist nicht gesichert, dass die Operationen auf diesen Datentypen unteilbar sind. Es kann also passieren, dass ein Thread mitten in einer long- oder double-Operation von einem anderen Thread verdrängt wird. Greifen zwei Threads auf die gleiche lange Variable zu, so könnte es passieren, dass der eine Thread eine Hälfte schreibt und der andere Thread die andere.


Beispiel Eine long-Variable l ist mit dem Wert 0x12345678 90abcdefL initialisiert. Aus Gründen der Übersichtlichkeit ist in der ersten Hälfte des long-Werts ein Leerzeichen eingefügt. Jetzt versuchen zwei Threads gleichzeitig, die Variable zu ändern.
l = 0x00000000 00000000L;             l = 0xabcdef12 34567890L;

Wenn hier der erste Thread den ersten Teil der long-Variablen mit 00000000 beschreibt und dann ein Kontextwechsel stattfindet, ist die Variable fälschlicherweise mit 00000000 90abcdefL initialisiert. Kommt nun nach dem Kontextwechsel der zweite Thread, so kann dieser vielleicht auch nur den ersten Teil der Variable mit abcdef12 beschreiben, so dass danach die Variable den völlig krummen Wert abcdef12 90abcdefL enthält.


Um das zu vermeiden, können die Objekt- und Klassenvariablen mit dem Modifizierer volatile deklariert werden. Die Zugriffsoperationen werden auf diesen Variablen dann atomar ausgeführt. Der Modifizierer ist bei lokalen Variablen nicht gestattet. Das Schlüsselwort volatile findet sich im Quellcode sehr selten, und in der ersten Java-Betaversion wurde es überhaupt nicht benutzt.

Zwischenspeicherung untersagen

volatile beugt zusätzlich einem anderen Problem vor. Während der Berechnung könnte die Laufzeitumgebung Inhalte von Variablen im Prozessorspeicher (zum Beispiel Register) zwischengespeichert haben. Das passiert etwa, wenn in einer Schleife ein Wert immer hoch gezählt wird, der in einer Objektvariablen gespeichert wird.

Eine Laufzeitumgebung könnte den Zugriff auf Objektvariablen optimieren, indem zuerst der Variableninhalt in eine interne lokale Variable kopiert und anschließend, nach einer Berechnung, das Ergebnis zurückgespeichert wird. Ist cnt eine Objektvariable, dann könnte folgende Schleife von der Laufzeitumgebung optimiert werden:

for ( int i = 0; i < 1000000; i++ )
  cnt++;

Eine optimierende Laufzeitumgebung könnte nun auf die Idee kommen, nicht bei jedem Schleifendurchlauf die Objektvariable zu erhöhen, sondern nur zum Ende. Wenn die Umgebung zuerst i in eine interne lokale Variable kopiert, dann die gesamte Schleife ausführt und erst anschließend den internen Wert nach i zurückkopiert, haben wir viel Zeit gespart, denn der Zugriff auf eine lokale Variable, die im Register des Prozessors leben kann, ist wesentlich schneller als der Zugriff auf eine Objektvariable. Dies ist im Übrigen eine beliebte Strategie, um die Performanz eines Programms zu steigern. Doch mit dieser internen Optimierung kommt es zu schwer kontrollierbaren Nebeneffekten, und Änderungen am Wert von i sind nicht sofort für andere Threads sichtbar, denen damit die Information über die einzelnen Inkrementschritte fehlt.

Auch hier hilft wieder das ungewöhnliche Schlüsselwort. Ist die Variable mit volatile gekennzeichnet, wird das Ergebnis nicht im Zwischenspeicher belassen, sondern immer aktualisiert. Die parallelen Threads sehen somit immer den korrekten Variablenwert, da er vor jeder Benutzung aus dem Speicher gelesen und nach einer Änderung sofort wieder zurückgeschrieben wird.





Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press GmbH, Gartenstraße 24, 53229 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de