Entdecken Sie Millionen von E-Books, Hörbüchern und vieles mehr mit einer kostenlosen Testversion

Nur $11.99/Monat nach der Testphase. Jederzeit kündbar.

Java Core Programmierung: Memory Model und Garbage Collection
Java Core Programmierung: Memory Model und Garbage Collection
Java Core Programmierung: Memory Model und Garbage Collection
eBook346 Seiten3 Stunden

Java Core Programmierung: Memory Model und Garbage Collection

Bewertung: 0 von 5 Sternen

()

Vorschau lesen

Über dieses E-Book

Für Java-Entwickler sind nicht nur die Sprachelemente der Programmiersprache Java und die umfangreichen Bibliotheken des JDK (Java Development Kit) von Bedeutung; auch die Ablaufumgebung (JVM = Java Virtual Machine) ist relevant für die Software-Entwicklung in Java. Das vorliegende Buch "Java Core Programmierung" befasst sich mit zwei grundlegenden und wichtigen Aspekten der Java-Ablaufumgebung: dem Java Memory Model (JMM) und der Freispeicherverwaltung (Garbage Collection.)

Beim Java Memory Model geht es um die Regeln und Garantien für konkurrierende Zugriffe auf Java-Objekte in Multithread-Programmen. Es werden u.a. folgende Fragestellungen betrachtet: Wann und unter welchen Umständen werden Modifikationen, die ein Thread an einem Java-Objekt gemacht hat, anderen Threads sichtbar? Was genau ist der Effekt von Sprachmitteln wie synchronized, volatile und final? Was ist eine CAS-Operation? Wofür werden atomare Variablen benötigt? Was ist Lock-Free-Programming und warum könnte es mich als Java-Entwickler interessieren?
Schon seit Langem ist das Memory Model Bestandteil der Sprachspezifikation von Java. Es ist aber viele Jahre lang wenig beachtet worden. Erst seit Multi-CPU- und Multicore-Plattformen vorherrschend sind, gewinnt das Wissen über die Details des Memory Models an Bedeutung. Detaillierte Kenntnisse des Memory Models sind unerlässlich für alle Java-Entwickler, die Java-Anwendungen für Multi-CPU- und Multicore-Hardware entwickeln und sich mit der Optimierung dieser Anwendungen hinsichtlich Skalierbarkeit und Performanz befassen.

Zwar geht es auch beim Thema Freispeicherverwaltung um den "Speicher", aber mit dem Java Memory Model hat die Garbage Collection nichts zu tun. Es geht vielmehr um die Strategien, die eine virtuelle Maschine anwendet, um den im Programm mit new angeforderten Freispeicher zu verwalten und ihn wieder frei zu geben, wenn er nicht mehr benötigt wird. Virtuelle Maschinen verwenden dazu unterschiedliche Algorithmen. Im vorliegenden Buch werden die Garbage-Collection-Algorithmen der populären HotSpot-JVM von Sun (heute Oracle) betrachtet. Es wird erläutert, warum Generational Garbage Collection sinnvoll ist, wie die HotSpot-JVM den Freispeicher aufteilt und wie die parallelen und konkurrierenden Garbage-Collection-Algorithmen funktionieren. Auch der relativ neue "Garbage-First" (G1) Collector wird vorgestellt. Ein wesentliches Augenmerk liegt dabei auf dem Tuning dieser zahlreichen Garbage-Collection-Algorithmen. Es lohnt sich beispielsweise zu prüfen, wie viel von der gesamten CPU-Zeit einer Java-Applikation für die Garbage-Collection aufgewendet werden muss. Idealerweise möchte man einen möglichst hohen Durchsatz haben, d.h. es soll viel CPU-Zeit für die Anwendung zur Verfügung stehen und nur wenig Zeit für die Freispeicherverwaltung aufgewendet werden. Auch sollte die Garbage Collection nicht zu störenden Pausen führen, in denen sämtliche Applikations-Threads zum Zwecke der Garbage Collection angehalten werden. Um die Maximierung des Durchsatzes und die Minimierung der Pausenzeiten zu erreichen, bietet die HotSpot-JVM zahlreiche Tuning-Möglichkeiten.

Das Buch basiert auf einer Artikelserie, die die Autoren im Java Magazin in den Jahren 2008 bis 2011 veröffentlicht haben. Weitere Beiträge der Artikelreihe sind unter http://www.angelikalanger.com/Articles/EffectiveJava.html zu finden.
SpracheDeutsch
Herausgeberentwickler.press
Erscheinungsdatum20. Aug. 2011
ISBN9783868026023
Java Core Programmierung: Memory Model und Garbage Collection

Mehr von Angelika Langer lesen

Ähnlich wie Java Core Programmierung

Ähnliche E-Books

Programmieren für Sie

Mehr anzeigen

Ähnliche Artikel

Rezensionen für Java Core Programmierung

Bewertung: 0 von 5 Sternen
0 Bewertungen

0 Bewertungen0 Rezensionen

Wie hat es Ihnen gefallen?

Zum Bewerten, tippen

Die Rezension muss mindestens 10 Wörter umfassen

    Buchvorschau

    Java Core Programmierung - Angelika Langer

    Angelika Langer, Klaus Kreft

    Java-Core-Programmierung

    ISBN: 978-3-86802-602-3

    © 2012 entwickler.press

    Ein Imprint der Software & Support Media GmbH

    Bibliografische Information Der Deutschen Bibliothek

    Die Deutsche Bibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.ddb.de abrufbar.

    Ihr Kontakt zum Verlag und Lektorat:

    Software & Support Media GmbH

    entwickler.press

    Darmstädter Landstr. 108

    60598 Frankfurt am Main

    Tel.: +49 (0)69 630089-0

    Fax: +49 (0)69 930089-89

    lektorat@entwickler-press.de

    http://www.entwickler-press.de

    Lektorat: Sebastian Burkart

    Korrektorat: Katharina Klassen und Lisa Pychlau

    Satz: Dominique Kalbassi

    Alle Rechte, auch für Übersetzungen, sind vorbehalten. Reproduktion jeglicher Art (Fotokopie, Nachdruck, Mikrofilm, Erfassung auf elektronischen Datenträgern oder anderen Verfahren) nur mit schriftlicher Genehmigung des Verlags. Jegliche Haftung für die Richtigkeit des gesamten Werks kann, trotz sorgfältiger Prüfung durch Autor und Verlag, nicht übernommen werden. Die im Buch genannten Produkte, Warenzeichen und Firmennamen sind in der Regel durch deren Inhaber geschützt.

    Vorwort

    Wir haben im Jahr 2002 mit der Veröffentlichung einer Artikelreihe begonnen, die bis heute Themen und Aspekte aufgreift, die den Kern der Programmiersprache Java und ihr Laufzeitsystem betreffen. Es ging uns dabei um Inhalte, die für jeden Java-Entwickler relevant sind, ganz gleich welche Art von Java-Applikation (Client, Server, Desktop, Enterprise etc.) er entwickelt. Im Jahr 2011 ist der Verleger des vorliegenden Buches an uns herangetreten mit der Bitte, die Beiträge der letzten Jahre (2008 bis 2011) als Buch veröffentlichen zu dürfen. In dieser Zeit haben wir uns mit dem Java-Memory-Modell und mit den Garbage-Collection-Mechanismen der Sun JVM befasst. Da es zu diesen Themen keine Veröffentlichungen gibt, die ähnlich umfassend und detailliert sind, haben wir dem Buchprojekt zugestimmt.

    Bei der Überarbeitung der Artikel für die Veröffentlichung als Buch haben wir uns entschlossen, den Artikelcharakter der Beiträge im Wesentlichen bei zu behalten. Die einzelnen Kapitel des Buchs sind deshalb so angelegt, dass sie zwar aufeinander Bezug nehmen, aber nicht notwendig der Reihe nach gelesen werden müssen. Es ist durchaus möglich und auch beabsichtigt, dass der Leser selektiv einzelne Kapitel lesen kann. Nachteil dieser Vorgehensweise ist ein gewisses Maß an Redundanz, das wir aber im Hinblick auf die beabsichtigte Verwendung der Texte für angemessen halten.

    Generell haben wir uns bemüht, Anglizismen zu vermeiden. Gelegentlich haben wir aber von Lesern der Artikelserie Einwände gegen die Verwendung von englischen Begriffen bekommen. Dabei handelt es sich stets um Fachbegriffe, deren Übersetzung ins Deutsche zwar prinzipiell möglich, aber in der Praxis wohl eher irreführend als hilfreich ist. Wie sollte man beispielsweise den Begriff „Remembered Set übersetzen, als „erinnerte Menge? Uns ist kein deutschsprachiger Text bekannt, der für diesen Fachbegriff aus der Garbage Collection eine Übersetzung verwendet hätte. Deshalb haben wir es auch nicht getan.

    Wir wünschen viel Spaß beim Lesen und Stöbern in den Details des Java-Memory-Modells und der Garbage Collection. Für Kommentare, Fragen und Anregungen sind wir unter http://www.AngelikaLanger.com/Forms/Contact.html zu erreichen.

    München, den 5. Juli 2011

    Angelika Langer und Klaus Kreft

    Teil A – Java-Memory-Modell

    Im ersten Teil des Buchs erläutern wir Aspekte des Java-Memory-Modells (JMM). Generell wird das Wissen über das Memory-Modell in Java für die Programmierung mit mehreren Threads gebraucht. Detaillierte Kenntnisse des Memory-Modells werden insbesondere durch die zunehmende Verwendung von Multicore-Prozessoren immer wichtiger. Das Wissen über das Memory-Modell wird daher zunehmend als Teil der Allgemeinbildung eines jeden Java-Entwicklers angesehen. Deshalb wollen wir in diesem ersten Teil des Buchs einige der wesentlichen Aspekte des Java-Memory-Modells erläutern.

    Um einen ersten Einblick in das Memory-Modell zu geben, betrachten wir in Kapitel 1 die Verwendung des volatile-Schüsselworts. Es wird gelegentlich zur Optimierung benutzt, um die „teure Synchronisation mit Locks durch eine „preiswertere Alternative mit volatile-Variablen zu ersetzen. Dabei kann der Entwickler leicht Fehler machen, wenn ihm das Java-Memory-Modell nicht geläufig ist. In diesem Kapitel sollen folgende Fragen beantwortet werden: Was bedeutet volatile? Wofür braucht man es? Worauf muss man achten?

    In Kapitel 2 erläutern wir die Prinzipien des Java-Memory-Modells. Es definiert Konsistenzregeln für den konkurrierenden Zugriff auf veränderliche Daten und Regeln für die Atomarität von Zugriffen auf gemeinsam verwendete Daten, deren Reihenfolge und die Sichtbarkeit etwaiger Modifikationen an den Daten. Dahinter steht ein mentales Modell, bei dem es einen Hauptspeicherbereich gibt, der für alle Threads sichtbar ist. Zusätzlich hat jeder Thread einen eigenen Cache, den nur er selbst sehen kann. Die Regeln des Java-Memory-Modells kann man sich anhand dieses Bildes als Garantien für Flushes und Refreshes der Thread-lokalen Caches vorstellen.

    In Kapitel 3 gehen wir auf die Kosten der Synchronisation und ihre Schwächen in Bezug auf Performance und Skalierbarkeit ein. Wir erläutern, warum man zum Zweck der Optimierung von Performance und Skalierbarkeit in zunehmendem Maße die Synchronisation durch andere Techniken ersetzen kann. Diese alternativen Techniken basieren unter anderem auf der Verwendung von volatile-, final- und atomaren Variablen. Deshalb werden in den folgenden Kapiteln die Memory-Modell-Regeln für volatile-, final- und für atomare Variablen besprochen.

    In Kapitel 4 befassen wir uns im Detail mit volatile-Variablen. Es geht um die Speichereffekte, die durch Zugriffe auf volatile-Variablen ausgelöst werden, die Sichtbarkeitsgarantien, die das Java-Memory-Modell für volatile-Variablen gibt, und die Kosten von volatile im Vergleich zur Synchronisation. Von besonderem Interesse sind dabei volatile-Referenzvariablen, weil man die Sichtbarkeitsgarantien meist nicht nur für die Referenz selbst, sondern auch für das referenzierte Objekt haben möchte.

    Kapitel 5 betrachtet ein Beispiel aus der Praxis, das zeigt, wie die Verwendung von volatile die Skalierbarkeit verbessert. Es geht um das häufig diskutierte Double-Check-Idiom und seine Varianten.

    In Kapitel 6 gehen wir der Frage nach, wann es sinnvoll ist, volatile als Alternative zu Locks zu verwenden. Prinzipiell sind volatile-Variablen preiswerter als die Synchronisation mit Locks. Aber nicht in allen Situationen kann man Synchronisation durch volatile-Variablen ersetzen, denn volatile-Variablen haben eine Reihe von Limitationen. Wir gehen deshalb der Frage nach, wann besser Synchronisation verwendet werden sollte.

    In Kapitel 7 erläutern wir die sogenannte Initialisation-Safety-Garantie für final-Felder. Es geht dabei um Garantien für die Sichtbarkeit im Zusammenhang mit der Initialisierung und dem Lesezugriff auf final-Felder. Diese Garantien werden bei der Implementierung von unveränderlichen Typen benötigt.

    Manchmal unterstellen Java-Programmierer, dass beim Zugriff auf Objekte von einem unveränderlichen Typ grundsätzlich keine Synchronisation und auch kein volatile erforderlich sei. Das ist leider ein Irrtum. In Kapitel 8 diskutieren wir einige typische Missverständnisse und Fehler.

    Die Verwendung von volatile als Ersatz für Synchronisation hat gewisse Grenzen. So ist es beispielsweise nicht möglich, eine atomare Read-Modify-Write-Sequenz auf einer volatile-Variablen auszuführen; es sind nur die einzelnen Lese- und Schreibzugriffe atomar, nicht aber die ganze Sequenz. Als logische Verallgemeinerung von volatile-Variablen gibt es seit Java 5.0 atomare Variablen, die ununterbrechbare Read-Modify-Write-Sequenzen unterstützen. In Kapitel 9 und 10 sehen wir uns die atomaren Variablen und ihre so genannten CAS-(Compare-and-Swap-)Operationen an.

    Als Abschluss der Ausführungen zum Java-Memory-Modell nehmen wir in Kapitel 11 die CopyOnWriteArrayList aus dem JDK und zeigen an ihrem Beispiel das Zusammenspiel der Speichereffekte von Synchronisation, volatile und atomaren Referenzen. Letztlich wird verdeutlicht, welche Zusammenhänge die unterschiedlichen und zuvor ausführlich besprochenen Aspekte in der Praxis haben.

    1 Einführung in das Java-Memory-Modell

    Um einen ersten Einblick in das Memory-Modell zu geben, betrachten wir die Verwendung des volatile-Schüsselworts. Es wird gelegentlich als Optimierung benutzt, um die teure Synchronisation mit Locks durch eine preiswertere Alternative mit volatile-Variablen zu ersetzen. Dabei kann man als Entwickler aber leicht Fehler machen, wenn einem das Java-Memory-Modell nicht geläufig ist. Daher wird im Folgenden darauf eingegangen, was volatile bedeutet, wofür man es braucht und worauf man achten muss.

    Unter Java-Programmierern ist allgemein bekannt, dass man bei der Programmierung mit mehreren Threads besonders aufpassen muss, wenn diese parallel ablaufenden Threads auf gemeinsam verwendete, veränderliche Daten (Shared Mutable Data) zugreifen. Dabei gibt es eine Reihe von Aspekten, die man als Programmierer im Auge behalten muss. Das bekannteste Problem ist die Race Condition: Wenn der Zugriff auf die gemeinsam verwendeten Daten in mehreren Schritten erfolgt, dann ist der Zugriff unterbrechbar. Ein Thread etwa macht die ersten Schritte des Zugriffs, wird mittendrin vom Thread Scheduler verdrängt, ein anderer Thread kommt zum Zug, greift auf die halbveränderten Daten zu und modifiziert sie womöglich, wird seinerseits unterbrochen, der erste Thread kommt wieder dran. Er macht weiter, als sei nichts gewesen. Das Resultat dieser konkurrierenden Zugriffe ist unvorhersehbar.

    Die übliche Abhilfe ist Synchronisation. Mithilfe von einem Lock (auch Mutex genannt) wird dafür gesorgt, dass der Zugriff auf die gemeinsam verwendeten veränderlichen Daten ununterbrechbar ist. Das funktioniert folgendermaßen: Alle beteiligten Threads, die auf die Daten zugreifen wollen, benutzen ein bestimmtes Lock-Objekt. Vor jedem Zugriff auf die kritischen Daten wird das Lock angefordert, nach dem Beenden aller Zugriffsschritte wird das Lock wieder freigegeben. Da ein Lock immer nur einen Thread als Besitzer haben kann, muss beim Anfordern gewartet werden, bis der aktuelle Besitzer das Lock wieder freigibt. Auf diese Weise ist gesichert, dass immer nur ein Thread zu einer Zeit auf die gemeinsam verwendeten veränderlichen Daten zugreifen kann, weil das Lock besetzt ist und alle anderen Threads warten müssen, bis der Thread fertig ist mit seinem Zugriff.

    Diese Grundlagen sind sicher jedem Java-Entwickler geläufig. Denn die Instrumente für die Synchronisation sind in Java direkt in die Sprache eingebettet worden: zum einen in Form des synchronized-Schlüsselworts und zum anderen durch das automatische Anhängen eines Locks an jedes Objekt, das man zwar nicht sieht, das aber immer vorhanden ist und implizit über das synchronized-Schlüsselwort angesprochen wird. Als Alternative gibt es seit Java 5 auch die flexibleren, expliziten Locks (siehe Interface Lock im Package java.util.concurrent.locks).

    Nun ist die Verwendung von Locks aber teuer, und deswegen kommt als Optimierung das volatile-Schlüsselwort ins Spiel. Die Kosten der Synchronisation mithilfe von Locks bestehen zum einen im Aufwand, den das Anfordern und Freigeben von Locks für die virtuelle Maschine und das Betriebsystem bedeuten, und zum anderen in der Tatsache, dass Synchronisation Wartezustände auslöst, die den Durchsatz der Anwendung reduzieren.

    Beim Anfordern und Freigeben von Locks hat das Laufzeitsystem nämlich ein Menge zu tun: Es werden Systemressourcen angelegt und weggeräumt, es werden Threads in Warteschlangen gestellt oder aus Wartezuständen aufgeweckt, es passieren Kontextwechsel und es müssen Daten-Caches abgeglichen werden. All das führt dazu, dass Synchronisation Zeit und Aufwand kostet, der die Performance der Anwendung reduziert.

    Daneben wirkt sich Synchronisation nachteilig auf den Durchsatz der Anwendung aus. Es kann passieren, dass der synchronisierte Zugriff auf gemeinsam verwendete veränderliche Daten zu einem echten Engpass werden kann. Wenn viele Threads gleichzeitig auf gemeinsam verwendete Daten zugreifen wollen und immer nur einer das Lock bekommt und alle anderen warten müssen, dann entsteht ein Stau, der sich negativ auf den Durchsatz der Anwendung auswirkt. Mit anderen Worten, Synchronisation skaliert nicht beliebig, und je weniger Synchronisation gebraucht wird, desto besser ist es. Also sollte man die Synchronisation reduzieren, wo immer es geht.

    1.1 Atomare Zugriffe

    Wie oben erläutert, wird die Synchronisation gebraucht, um komplexere Zugriffe auf gemeinsam verwendete veränderliche Daten ununterbrechbar zu machen. Wenn die betreffenden Daten aber elementar sind und der Zugriff darauf schon von Natur aus ununterbrechbar (also atomar) ist, dann braucht man doch keine Synchronisation, richtig? Wenn also zum Beispiel die gemeinsam verwendeten Daten lediglich aus einem boolean bestehen, dann sind Zugriffe wie Lesen oder Schreiben atomar. Das garantiert die Sprachdefinition (siehe /JLS/, Kapitel 17). Dort ist festgelegt, dass lesende und schreibende Zugriffe auf Variablen von primitivem Typ (außer long und double) atomar sind. Deshalb wird in der Praxis vielfach die Synchronisation weggelassen, wenn es um konkurriende Zugriffe auf Variablen von primitivem Typ geht. Hier ist ein typisches Beispiel:

    public class Processor {

        private boolean connectionPrepared = false;

        public void prepareConnection() {

            // ... open connection ...

            connectionPrepared = true;

        }

        public void start() throws InterruptedException {

            // ... various initializations ...

            while (!connectionPrepared )

                Thread.sleep(500);

            // ... start actual processing ...

        }

    }

    Wir haben hier zwei Methoden der Klasse Processor, die beide auf das Feld connectionPrepared zugreifen. Die Idee ist, dass die start-Methode in einem Thread ausgeführt wird, der alle vorbereitenden Arbeiten anstößt und dann abwartet, bis alle Vorbereitungen abgeschlossen sind, ehe er die eigentliche Verarbeitung beginnt. Die beiden Threads kommunizieren miteinander über gemeinsam verwendete veränderliche Daten, nämlich das boolean-Feld connectionPrepared. Der eine Thread setzt connectionPrepared auf true, wenn er fertig ist. Der andere Thread beobachtet, ob connectionPrepared auf true gesetzt wurde, um dann mit der eigentlichen Arbeit zu beginnen. Das Lesen und Verändern des boolean-Feldes ist garantiert atomar, deshalb wird keine Synchronisation verwendet. Leider wurde hier ein wesentlicher Aspekt übersehen.

    1.2 Sequential Consistency

    Der Autor dieser kleinen Klasse ist augenscheinlich davon ausgegangen, dass der eine Thread irgendwann einmal das boolean-Feld connectionPrepared setzen wird und dass der andere Thread den veränderten Wert dann sehen kann. Eine derartige Garantie gibt es in Java aber nicht. Es ist keineswegs so, dass ein Thread immer sofort sehen kann, was ein anderer Thread im Speicher gemacht hat. Es kann passieren, dass der eine Thread das boolean-Feld connectionPrepared auf true gesetzt hat und der andere Thread diese Änderung nie zu sehen bekommt.

    Das Schwierige an der Sache ist, dass der beschriebene Effekt nicht auftreten muss, aber auftreten kann. Das heißt, die Klasse hat einen Fehler, der sich unter Umständen nicht bemerkbar macht. In manchen existierenden Anwendungen schlummern derartige Fehler, die bisher nicht entdeckt wurden. Nun ist es so, dass auf Maschinen mit nur einem Single-Core-Prozessor die Fehler häufig nicht auftreten. In einer Multi-Prozessor- oder Multicore-Umgebung kann der Fehler jedoch plötzlich auftreten. Da heute Dual-Core-Prozessoren Standard sind, kann man in der Tat beobachten, wie Anwendungen, die auf einer Single-Core-Maschine tadellos funktioniert haben, unerwartet seltsame Fehler aufweisen, sobald sie auf einer Maschine mit einem Multi-Core-Prozessor ablaufen.

    Welchen Fehler hat der Autor der Klasse gemacht? Er hat Annahmen über das Verhalten der virtuellen Maschine gemacht, die unzutreffend sind. Was er unterstellt hat, wird allgemein als Sequential Consistency bezeichnet. Sequential Consistency bedeutet, dass ein Thread, der später drankommt, sehen kann, was die Threads vor ihm im Speicher an den gemeinsam verwendeten Daten gemacht haben. Das ist ein wunderbar simples mentales Modell, das aber leider von Java nicht unterstützt wird. Wir haben a priori keine Sequential Consistency in Java.

    Es gibt in der Sprachspezifikation stattdessen eine Reihe von Garantien für die Reihenfolge von Operationen und auch für die Sichtbarkeit von Memory-Modifikationen. Sie sind jedoch wesentlich schwächer als die unserer Intuition entsprechende Sequential Consistency. Wir wollen jetzt nicht das gesamte Memory-Modell von Java aufrollen, sondern nur eine einzige der Regeln aus der Sprachspezifikation herausgreifen, nämlich die Garantien für volatile-Variablen.

    1.3 Sichtbarkeitsregeln für volatile-Variablen

    Für eine volatile-Variable ist garantiert, dass ein Thread, der die Variable liest, den Wert bekommt, den zuvor ein anderer Thread derselben Variablen zugewiesen hat. Es ist außerdem garantiert, dass der Wert, den ein Thread in einer volatile-Variablen ablegt, allen anderen Threads zugänglich gemacht wird. Das heißt für volatile-Variablen haben wir die gewünschte Sequential Consistency. Allerdings gilt das nur für die Variable selbst: Bei einer Variablen von einem Referenztyp gilt es nur für die Adresse, nicht für das referenzierte Objekt. Die Garantien sind streng genommen sogar noch umfangreicher: Beim Schreiben auf eine volatile-Variable wird nicht nur der neue Inhalt eben jener volatile-Variablen sichtbar gemacht, sondern alle Modifikation, die der Thread zuvor im Speicher gemacht hat. Das interessiert uns aber im Moment nicht. Da das Thema etwas umfangreicher ist, werden wir die Details an einer anderen Stelle näher erläutern. Kehren wir lieber zu unserem fehlerhaften Beispielcode zurück.

    Man könnte den Fehler ganz einfach dadurch beheben, dass man das boolean-Feld overHeated als volatile deklariert:

    public class Processor {

        private volatile boolean connectionPrepared = false;

        public void prepareConnection() {

            // ... open connection ...

            connectionPrepared = true;

        }

        public void start() throws InterruptedException {

            // ... various initializations ...

            while (!connectionPrepared )

                Thread.sleep(500);

            // ... start actual processing ...

        }

    }

    Jetzt ist die Klasse korrekt, weil nun garantiert ist, dass die Modifikation, die der eine Thread mithilfe der prepareConnection-Methode am volatile boolean connectionPrepared vornimmt, dem anderen Thread sichtbar gemacht wird. Mit dieser volatile-Deklaration kann es nicht mehr passieren, dass der eine Thread das boolean-Feld ändert und der andere Thread es gar nicht mitbekommt.

    1.4 Sichtbarkeitsregeln für Synchronisation

    Wir wollen nicht verschweigen,

    Gefällt Ihnen die Vorschau?
    Seite 1 von 1