Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Neues in Java 7
2 Threads und nebenläufige Programmierung
3 Datenstrukturen und Algorithmen
4 Raum und Zeit
5 Dateien, Verzeichnisse und Dateizugriffe
6 Datenströme
7 Die eXtensible Markup Language (XML)
8 Dateiformate
9 Grafische Oberflächen mit Swing
10 Grafikprogrammierung
11 Netzwerkprogrammierung
12 Verteilte Programmierung mit RMI
13 RESTful und SOAP Web-Services
14 JavaServer Pages und Servlets
15 Applets
16 Datenbankmanagement mit JDBC
17 Technologien für die Infrastruktur
18 Reflection und Annotationen
19 Dynamische Übersetzung und Skriptsprachen
20 Logging und Monitoring
21 Java Native Interface (JNI)
22 Sicherheitskonzepte
23 Dienstprogramme für die Java-Umgebung
Stichwort
Download: - openbook, ca. 21,3 MB
Buch bestellen
Ihre Meinung?
<< zurück | Galileo Computing / <openbook> / Java 7 - Mehr als eine Insel | vor >>
Java 7 - Mehr als eine Insel von Christian Ullenboom Das Handbuch zu den Java SE-Bibliotheken
Java 7 - Mehr als eine Insel Galileo Computing 1433 S., 2012, geb. 49,90 Euro, ISBN 978-3-8362-1507-7 |
| 2 Threads und nebenläufige Programmierung
|  | 2.1 Threads erzeugen
|  |  | 2.1.1 Threads über die Schnittstelle Runnable implementieren
|  |  | 2.1.2 Thread mit Runnable starten
|  |  | 2.1.3 Die Klasse Thread erweitern
|  | 2.2 Thread-Eigenschaften und -Zustände
|  |  | 2.2.1 Der Name eines Threads
|  |  | 2.2.2 Wer bin ich?
|  |  | 2.2.3 Die Zustände eines Threads *
|  |  | 2.2.4 Schläfer gesucht
|  |  | 2.2.5 Mit yield() auf Rechenzeit verzichten
|  |  | 2.2.6 Der Thread als Dämon
|  |  | 2.2.7 Das Ende eines Threads
|  |  | 2.2.8 Einen Thread höflich mit Interrupt beenden
|  |  | 2.2.9 UncaughtExceptionHandler für unbehandelte Ausnahmen
|  |  | 2.2.10 Der stop() von außen und die Rettung mit ThreadDeath *
|  |  | 2.2.11 Ein Rendezvous mit join() *
|  |  | 2.2.12 Arbeit niederlegen und wieder aufnehmen *
|  |  | 2.2.13 Priorität *
|  | 2.3 Der Ausführer (Executor) kommt
|  |  | 2.3.1 Die Schnittstelle Executor
|  |  | 2.3.2 Die Thread-Pools
|  |  | 2.3.3 Threads mit Rückgabe über Callable
|  |  | 2.3.4 Mehrere Callable abarbeiten
|  |  | 2.3.5 ScheduledExecutorService für wiederholende Ausgaben und Zeitsteuerungen nutzen
|  | 2.4 Synchronisation über kritische Abschnitte
|  |  | 2.4.1 Gemeinsam genutzte Daten
|  |  | 2.4.2 Probleme beim gemeinsamen Zugriff und kritische Abschnitte
|  |  | 2.4.3 Punkte parallel initialisieren
|  |  | 2.4.4 i++ sieht atomar aus, ist es aber nicht *
|  |  | 2.4.5 Kritische Abschnitte schützen
|  |  | 2.4.6 Kritische Abschnitte mit ReentrantLock schützen
|  |  | 2.4.7 Synchronisieren mit synchronized
|  |  | 2.4.8 Synchronized-Methoden der Klasse StringBuffer *
|  |  | 2.4.9 Mit synchronized synchronisierte Blöcke
|  |  | 2.4.10 Dann machen wir doch gleich alles synchronisiert!
|  |  | 2.4.11 Lock-Freigabe im Fall von Exceptions
|  |  | 2.4.12 Deadlocks
|  |  | 2.4.13 Mit synchronized nachträglich synchronisieren *
|  |  | 2.4.14 Monitore sind reentrant – gut für die Geschwindigkeit *
|  |  | 2.4.15 Synchronisierte Methodenaufrufe zusammenfassen *
|  | 2.5 Synchronisation über Warten und Benachrichtigen
|  |  | 2.5.1 Die Schnittstelle Condition
|  |  | 2.5.2 It’s Disco-Time *
|  |  | 2.5.3 Warten mit wait() und Aufwecken mit notify() *
|  |  | 2.5.4 Falls der Lock fehlt: IllegalMonitorStateException *
|  | 2.6 Datensynchronisation durch besondere Concurrency-Klassen *
|  |  | 2.6.1 Semaphor
|  |  | 2.6.2 Barrier und Austausch
|  |  | 2.6.3 Stop and go mit Exchanger
|  | 2.7 Atomare Operationen und frische Werte mit volatile *
|  |  | 2.7.1 Der Modifizierer volatile bei Objekt-/Klassenvariablen
|  |  | 2.7.2 Das Paket java.util.concurrent.atomic
|  | 2.8 Teile und herrsche mit Fork und Join *
|  |  | 2.8.1 Algorithmendesign per »teile und herrsche«
|  |  | 2.8.2 Paralleles Lösen von D&C-Algorithmen
|  |  | 2.8.3 Fork und Join
|  | 2.9 Mit dem Thread verbundene Variablen *
|  |  | 2.9.1 ThreadLocal
|  |  | 2.9.2 InheritableThreadLocal
|  |  | 2.9.3 ThreadLocalRandom als Zufallszahlengenerator
|  |  | 2.9.4 ThreadLocal bei der Performance-Optimierung
|  | 2.10 Threads in einer Thread-Gruppe *
|  |  | 2.10.1 Aktive Threads in der Umgebung
|  |  | 2.10.2 Etwas über die aktuelle Thread-Gruppe herausfinden
|  |  | 2.10.3 Threads in einer Thread-Gruppe anlegen
|  |  | 2.10.4 Methoden von Thread und ThreadGroup im Vergleich
|  | 2.11 Zeitgesteuerte Abläufe
|  |  | 2.11.1 Die Typen Timer und TimerTask
|  |  | 2.11.2 Job-Scheduler Quartz
|  | 2.12 Einen Abbruch der virtuellen Maschine erkennen
|  | 2.13 Zum Weiterlesen
## 2.7 Atomare Operationen und frische Werte mit volatile * Die JVM arbeitet bei den Ganzzahl-Datentypen kleiner gleich int intern nur mit einem int. Doch obwohl es auch für die 64-Bit-Datentypen long und double einzelne Bytecode-Befehle gibt, ist nicht gesichert, dass die Operationen auf diesen Datentypen unteilbar sind, da etwa eine 64-Bit-Zuweisung sich aus zwei 32-Bit-Zuweisungen zusammensetzen lässt. 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 64-Bit-Variable zu, so könnte möglicherweise der eine Thread eine Hälfte schreiben und der andere Thread die andere.
Beispiel
Zwei Threads versuchen gleichzeitig, die Variable l vom Typ long zu ändern (aus Gründen der Übersichtlichkeit ist ein Leerzeichen nach dem ersten 32-Bit-Block in der hexadezimalen Notation eingefügt):
    l = 0x00000000 00000000             l = 0xFFFFFFFF FFFFFFFF
Schreibt der erste Thread den ersten Teil der long-Variablen mit 00000000, und findet dann ein Kontextwechsel statt, sodass der zweite Thread die Variable komplett mit FFFFFFFF FFFFFFFF initialisiert, gibt es nach dem erneuten Umschalten ein Problem, weil dann der erste Thread seine zweite Hälfte schreiben wird und die Belegung der Variablen FFFFFFFF 00000000 ist. Insgesamt gibt es vier denkbare Belegungen durch das Scheduling beim Kontextwechsel:
    l = 0x00000000 00000000
    l = 0xFFFFFFFF FFFFFFFF
    l = 0x00000000 FFFFFFFF
    l = 0xFFFFFFFF 00000000
### 2.7.1 Der Modifizierer volatile bei Objekt-/Klassenvariablen Um dies 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, da sie auf dem Stapel liegen und für andere Threads nicht zugänglich sind. Achtung: Auch mit volatile sind Operationen wie i++ natürlich noch nicht atomar, da i++ aus Grundoperationen besteht: Lesen von i, Erhöhen um eins und Schreiben von i; Abschnitt 2.4.9, »Mit synchronized synchronisierte Blöcke«, nannte die Teile schon. #### 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 hochgezählt wird, der in einer Objektvariablen gespeichert wird. Eine Laufzeitumgebung könnte den (teureren) Zugriff auf Objektvariablen optimieren, indem sie zuerst den Variableninhalt in eine (schnelle) interne lokale Variable kopiert und anschließend, nach einer Berechnung, das Ergebnis zurückgespeichert. Nehmen wir eine Objektvariable cnt und folgende Schleife an:
    for ( int cnt = 0; cnt < 1000000; cnt++ )
      cnt++;
Eine optimierende Laufzeitumgebung könnte nun auf die Idee kommen, nicht bei jedem Schleifendurchlauf den Speicher der Objektvariable cnt auszulesen und zu beschreiben, sondern nur am Ende. Etwa so:
    int i = cnt;
    for ( int i = 0; i < 1000000; i++ )
      i++;
    cnt = i;
Wenn die Umgebung cnt zuerst in eine interne lokale Variable kopiert, dann die gesamte Schleife ausführt und erst anschließend den internen Wert nach cnt zurückkopiert, hätten wir viel Zeit gespart, denn der Zugriff auf eine lokale Variable, die sich im Register des Prozessors aufhalten kann, ist wesentlich schneller als der Zugriff auf eine Objektvariable. Dies ist im Übrigen eine beliebte Strategie, um die Performance eines Programms zu steigern. Mit dieser internen Optimierung kommt es jedoch zu schwer kontrollierbaren Nebeneffekten, und Änderungen am Wert von cnt sind nicht sofort für andere Threads sichtbar, denen damit die Information über die einzelnen Inkrementschritte fehlt. Oder ein anderes Beispiel:
    int i = cnt;
    Thread.sleep( 10000 );
    int j = cnt;
Verändert ein anderer Thread während des Wartens die Variable cnt, könnte j = i sein, wenn die Laufzeitumgebung die Variable j nicht frisch mit dem Wert aus cnt initialisiert. Nebenläufige Programmierung und Programmoptimierung passen schwer zusammen! Auch hier hilft wieder das ungewöhnliche Schlüsselwort. Ist die Variable mit volatile gekennzeichnet, wird das Ergebnis nicht im Zwischenspeicher belassen, sondern ständig 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. Sehr gut ist das für boolean-Werte, die Flags anzeigen, die andere Threads zur Synchronisation beobachten. Ein Array, das mit volatile markiert ist, hat alle Zugriffe auf die Elemente volatile, denn auch hier könnte die JVM die Elemente aus Performance-Gründen zwischenspeichern. Wohlgemerkt bleiben nur die Werte aktuell; nicht-atomare Operationen wie cnt++ sind damit immer noch nicht atomar. ### 2.7.2 Das Paket java.util.concurrent.atomic Während volatile das Lesen und Schreiben auf den 64-Bit-Datentypen atomar macht, ist eine Operation wie cnt++ oder cnt > 12 nicht atomar. Kurz vor dem Erhöhen oder dem Vergleich kann ein Thread-Wechsel stattfinden und cnt geändert werden, sodass der Vergleich einen anderen Ausgang findet. Die Rettung ist ein synchronisierter Block, wobei jedoch eine Synchronisationsvariable nötig ist. Seit Java 5 helfen hier die Klassen des Pakets java.util.concurrent.atomic. Ein Beispiel für einen veränderbaren Behälter ist AtomicInteger, der Operationen wie getAndIncrement() anbietet, die den Wert des Behälters atomar erhöhen.
Beispiel
Eine öffentliche Klasse bietet über eine statische Methode eine ID (im Bereich long) an. Listing 2.36: com/tutego/insel/thread/atomic/Id.java
    public class Id
    {
      private static final AtomicLong id = new AtomicLong();

      private Id() { /* Empty */ }

      public long next()
      {
        return id.getAndIncrement();
      }
    }
Insgesamt befinden sich im Paket java.util.concurrent.atomic folgende Klassen für elementare Datentypen: AtomicBoolean, AtomicInteger, AtomicLong – es gibt weder AtomicShort noch AtomicByte. Da diese Objekte alle veränderbar sind, eignen sie sich nicht unbedingt als Ersatz für immutable Wrapper-Objekte wie Integer, insbesondere nicht in allgemeinen Assoziativspeichern oder Mengen; sie deklarieren auch gar kein eigenes equals() und hashCode(). Für Felder stehen bereit: AtomicIntegerArray, AtomicLongArray. Die Klassen AtomicIntegerFieldUpdater<T> und AtomicLongFieldUpdater<T> gehen über Reflection an Attribute heran, die volatile sind, und ermöglichen atomare Operationen. Dazu kommen noch einige Klassen zum atomaren Arbeiten mit Referenzen: AtomicReference, AtomicReferenceArray<E>, AtomicReferenceFieldUpdater<T,V>, AtomicMarkableReference<V> (assoziiert ein Objekt mit einem Flag, das atomar geändert werden kann) und AtomicStampedReference<V> (assoziiert ein Objekt mit einer Ganzzahl). ### Ihr Kommentar Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück | <top> | vor >>
Zum Katalog
Java 7 – Mehr als eine Insel Jetzt bestellen
Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen? Ihre Meinung
Buchempfehlungen
| Java und XML
| Einstieg in Eclipse 3.7
| Android 3
| NetBeans Platform 7
| Java ist auch eine Insel
| Apps entwickeln für Android 4
| Java 7
Shopping
Versandkostenfrei bestellen in Deutschland und Österreich Info

Copyright © Galileo Press 2012
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das 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, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de