Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
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

Buch bestellen
Ihre Meinung?

Spacer
Java 7 - Mehr als eine Insel von Christian Ullenboom
Das Handbuch zu den Java SE-Bibliotheken
Buch: Java 7 - Mehr als eine Insel

Java 7 - Mehr als eine Insel
Rheinwerk Computing
1433 S., 2012, geb.
49,90 Euro, ISBN 978-3-8362-1507-7
Pfeil 2 Threads und nebenläufige Programmierung
Pfeil 2.1 Threads erzeugen
Pfeil 2.1.1 Threads über die Schnittstelle Runnable implementieren
Pfeil 2.1.2 Thread mit Runnable starten
Pfeil 2.1.3 Die Klasse Thread erweitern
Pfeil 2.2 Thread-Eigenschaften und -Zustände
Pfeil 2.2.1 Der Name eines Threads
Pfeil 2.2.2 Wer bin ich?
Pfeil 2.2.3 Die Zustände eines Threads *
Pfeil 2.2.4 Schläfer gesucht
Pfeil 2.2.5 Mit yield() auf Rechenzeit verzichten
Pfeil 2.2.6 Der Thread als Dämon
Pfeil 2.2.7 Das Ende eines Threads
Pfeil 2.2.8 Einen Thread höflich mit Interrupt beenden
Pfeil 2.2.9 UncaughtExceptionHandler für unbehandelte Ausnahmen
Pfeil 2.2.10 Der stop() von außen und die Rettung mit ThreadDeath *
Pfeil 2.2.11 Ein Rendezvous mit join() *
Pfeil 2.2.12 Arbeit niederlegen und wieder aufnehmen *
Pfeil 2.2.13 Priorität *
Pfeil 2.3 Der Ausführer (Executor) kommt
Pfeil 2.3.1 Die Schnittstelle Executor
Pfeil 2.3.2 Die Thread-Pools
Pfeil 2.3.3 Threads mit Rückgabe über Callable
Pfeil 2.3.4 Mehrere Callable abarbeiten
Pfeil 2.3.5 ScheduledExecutorService für wiederholende Ausgaben und Zeitsteuerungen nutzen
Pfeil 2.4 Synchronisation über kritische Abschnitte
Pfeil 2.4.1 Gemeinsam genutzte Daten
Pfeil 2.4.2 Probleme beim gemeinsamen Zugriff und kritische Abschnitte
Pfeil 2.4.3 Punkte parallel initialisieren
Pfeil 2.4.4 i++ sieht atomar aus, ist es aber nicht *
Pfeil 2.4.5 Kritische Abschnitte schützen
Pfeil 2.4.6 Kritische Abschnitte mit ReentrantLock schützen
Pfeil 2.4.7 Synchronisieren mit synchronized
Pfeil 2.4.8 Synchronized-Methoden der Klasse StringBuffer *
Pfeil 2.4.9 Mit synchronized synchronisierte Blöcke
Pfeil 2.4.10 Dann machen wir doch gleich alles synchronisiert!
Pfeil 2.4.11 Lock-Freigabe im Fall von Exceptions
Pfeil 2.4.12 Deadlocks
Pfeil 2.4.13 Mit synchronized nachträglich synchronisieren *
Pfeil 2.4.14 Monitore sind reentrant – gut für die Geschwindigkeit *
Pfeil 2.4.15 Synchronisierte Methodenaufrufe zusammenfassen *
Pfeil 2.5 Synchronisation über Warten und Benachrichtigen
Pfeil 2.5.1 Die Schnittstelle Condition
Pfeil 2.5.2 It’s Disco-Time *
Pfeil 2.5.3 Warten mit wait() und Aufwecken mit notify() *
Pfeil 2.5.4 Falls der Lock fehlt: IllegalMonitorStateException *
Pfeil 2.6 Datensynchronisation durch besondere Concurrency-Klassen *
Pfeil 2.6.1 Semaphor
Pfeil 2.6.2 Barrier und Austausch
Pfeil 2.6.3 Stop and go mit Exchanger
Pfeil 2.7 Atomare Operationen und frische Werte mit volatile *
Pfeil 2.7.1 Der Modifizierer volatile bei Objekt-/Klassenvariablen
Pfeil 2.7.2 Das Paket java.util.concurrent.atomic
Pfeil 2.8 Teile und herrsche mit Fork und Join *
Pfeil 2.8.1 Algorithmendesign per »teile und herrsche«
Pfeil 2.8.2 Paralleles Lösen von D&C-Algorithmen
Pfeil 2.8.3 Fork und Join
Pfeil 2.9 Mit dem Thread verbundene Variablen *
Pfeil 2.9.1 ThreadLocal
Pfeil 2.9.2 InheritableThreadLocal
Pfeil 2.9.3 ThreadLocalRandom als Zufallszahlengenerator
Pfeil 2.9.4 ThreadLocal bei der Performance-Optimierung
Pfeil 2.10 Threads in einer Thread-Gruppe *
Pfeil 2.10.1 Aktive Threads in der Umgebung
Pfeil 2.10.2 Etwas über die aktuelle Thread-Gruppe herausfinden
Pfeil 2.10.3 Threads in einer Thread-Gruppe anlegen
Pfeil 2.10.4 Methoden von Thread und ThreadGroup im Vergleich
Pfeil 2.11 Zeitgesteuerte Abläufe
Pfeil 2.11.1 Die Typen Timer und TimerTask
Pfeil 2.11.2 Job-Scheduler Quartz
Pfeil 2.12 Einen Abbruch der virtuellen Maschine erkennen
Pfeil 2.13 Zum Weiterlesen

Rheinwerk Computing - Zum Seitenanfang

2.9 Mit dem Thread verbundene Variablen *Zur nächsten Überschrift

Unterschiedliche Threads können ohne Probleme das gleiche Runnable ausführen, was auch ein übliches Szenario ist, wenn der Programmcode immer der gleiche ist. Doch auch wenn der Programmcode immer gleich bleibt, soll jedem Thread doch vielleicht ein eigener Speicherbereich zugeteilt werden, in dem er Informationen ablegen kann. Falls das Runnable-Objekt selbst eine Objektvariable hätte und pro Thread auf diese Weise ein neues Runnable-Objekt gebildet würde, wäre das kein Problem, doch müsste bei genau einem Runnable-Objekt und beliebig vielen Threads ein anderer Ort gefunden werden.


Rheinwerk Computing - Zum Seitenanfang

2.9.1 ThreadLocalZur nächsten ÜberschriftZur vorigen Überschrift

Die Lösung ist relativ einfach: Es müsste eine Datenstruktur geben, die jeden laufenden Thread mit einem Objekt oder einer Datenstruktur assoziiert. Diese Struktur muss dann aus dem Runnable-Objekt heraus referenzierbar sein. Java bietet mit der Klasse java.lang.ThreadLocal eine Implementierung für thread-lokale Variablen (engl. thread-local storage [TLS]) an.

Abbildung

Abbildung 2.12: UML-Diagramm von ThreadLocal

class java.lang.ThreadLocal<T>
  • ThreadLocal()
    Erzeugt eine neue thread-lokale Variable.
  • void set(T value)
    Setzt den Wert für den lokalen Thread.
  • T get()
    Liefert den Wert, der mit dem aktuellen Thread verbunden ist.
  • protected T initialValue()
    Die Methode kann überschrieben werden und liefert dann den Anfangswert beim ersten Aufruf von get() ohne vorangehendes set(). Der Standard ist null.
  • void remove()
    Entfernt den Wert der thread-lokalen Variablen, um etwa Speicher freizumachen. Ein anschließendes get() liefert wieder den Wert aus initialValue().

Zählen mit thread-lokalen Variablen

Das folgende Beispiel soll ein Runnable definieren, das endlos zählt. Der Clou ist aber, dass der Zähler keine lokale Variable, sondern eine thread-lokale Variable ist, die ThreadLocal verwaltet. Drei Threads sollen das gleiche Runnable abarbeiten: zwei neue Threads und der aktuell ausführende main-Thread:

Listing 2.41: com/tutego/insel/thread/ThreadLocalDemo.java

package com.tutego.insel.thread;

public class ThreadLocalDemo
{
public static void main( String[] args )
{
Runnable runnable = new SimpleRunnable();
new Thread( runnable ).start();
new Thread( runnable ).start();
runnable.run();
}
}

class SimpleRunnable implements Runnable
{
private static final ThreadLocal<Integer> mem =
new ThreadLocal<Integer>()
{
@Override protected Integer initialValue() { return 1; }
};

@Override public void run()
{
while ( true )
{
System.out.println( Thread.currentThread().getName() +
", " + mem.get() );
mem.set( mem.get() + 1 );
}
}
}

Den Startwert legt die überschriebene Methode initialValue() mit 1 fest. Da ThreadLocal einen Zähler speichert, ist der Datencontainer mit Integer typisiert.

Die Ausgabe kann beginnen mit:

main, 1
main, 2
main, 3
main, 4
main, 5
Thread-0, 1
Thread-1, 1
Thread-0, 2
Thread-1, 2
Thread-0, 3
Thread-1, 3
Entwicklungsumgebungen

Die IDE IntelliJ bietet die komfortable Möglichkeit, statische referenzierte nicht thread-sichere Objekte in ThreadLocal-Objekte zu konvertieren. Informationen dazu gibt es unter http://blogs.jetbrains.com/idea/2009/10/threadlocal-in-one-click/.

remove() nutzen

Jeder Thread speichert einen Assoziativspeicher mit allen thread-lokalen Variablen. Damit im ThreadLocal vergessene Werte nicht zum Problem werden, sollten sie mit remove() wieder entfernt werden. Sonst entstehen langlebige Objektreferenzen, die zu einem Speicherleck führen können. Denn in heutigen Umgebungen werden Threads nicht einfach so gestartet und beendet, sondern leben fast schon ewig in einem Thread-Pool und werden immer wieder recycelt. Der Tipp, der sich daraus ableitet, ist der folgende: Wenn thread-lokale Variablen nicht mehr benötigt werden, sollten sie entfernt werden, denn werden sie es nicht, verstopfen sie den Speicher. Das Problem ist nur, dass ein Thread im Thread-Pool ja gar nicht weiß, was für ein Runnable er abarbeitet, sodass es schwierig ist, Einträge wieder zu entfernen.[13](Sun hat das viele Jahre auch nicht vorgesehen. ThreadLocal gibt es seit Java 1.2, aber remove() erst seit Java 5.)


Rheinwerk Computing - Zum Seitenanfang

2.9.2 InheritableThreadLocalZur nächsten ÜberschriftZur vorigen Überschrift

Jeder Thread kann einen neuen Thread bilden, der dann Kind ist. Da die statische main-Methode selbst von einem Haupt-Thread ausgeführt wird, wie das Beispiel zeigt, ist schon dieser Thread der Vater, der neue Unter-Threads bildet.

Mit der Klasse InheritableThreadLocal, die eine Unterklasse von ThreadLocal ist, kann das Kind vom Vater den gespeicherten Wert übernehmen. Das würde sonst nicht funktionieren, da der neue Thread ja ganz eigene Werte hat, aber mit InheritableThreadLocal bleibt dieser Wert erhalten und wird auf die Kinder vererbt.

Dass ein gestarteter Kind-Thread den Wert vom Vater-Thread übernimmt, zeigt das folgende Programm. Ein Thread gibt den geerbten Wert aus und setzt anschließend einen neuen Wert für ein Kind, das der Thread in die Welt entlässt:

Listing 2.42: com/tutego/insel/thread/InheritableThreadLocalDemo.java

package com.tutego.insel.thread;

public class InheritableThreadLocalDemo
{
public static void main( String[] args )
{
new InheritingThread().start();
}
}

class InheritingThread extends Thread
{
// private static final ThreadLocal<String> mem = new ThreadLocal<String>();
private static final InheritableThreadLocal<String> mem =
new InheritableThreadLocal<String>();

@Override public void run()
{
System.out.println( Thread.currentThread() + " bekommt " + mem.get() );
mem.set( Thread.currentThread().getName() );

new InheritingThread().start();
}
}

Die Ausgabe beginnt mit:

Thread[Thread-0,5,main] bekommt null
Thread[Thread-1,5,main] bekommt Thread-0
Thread[Thread-2,5,main] bekommt Thread-1
Thread[Thread-3,5,main] bekommt Thread-2

Der erste Thread bekommt noch nichts von seinem Vater-Thread. Da jedoch der erste Thread den eigenen Namen in den Speicher von InheritableThreadLocal legt und dann der zweite neu gestartete Kind-Thread auf den Wert zugreift, empfängt er die Zeichenfolge »Thread-0«.

Wer zum Testen InheritableThreadLocal durch ThreadLocal ersetzt, der wird merken, dass das Beispiel so nicht funktioniert.


Rheinwerk Computing - Zum Seitenanfang

2.9.3 ThreadLocalRandom als ZufallszahlengeneratorZur nächsten ÜberschriftZur vorigen Überschrift

Zufallszahlen sind immer nur Pseudozufallszahlen und werden mit einer mathematischen Formel aus dem Vorgänger generiert. Der Vorgänger muss dabei gespeichert werden, und das ist die Aufgabe eines Random-Objekts. Die Methode Math.random() nutzt intern ein Random-Objekt, und jetzt kann es zu Wartezeiten kommen, wenn mehrere Threads gleichzeitig random() aufrufen, denn die Methode darf intern ja nur einen Thread die letzte Zufallszahl schreiben lassen. Um Zufallszahlen schnell generieren zu können, sind diese Verzögerungen ungünstig, und es gibt zwei Lösungen dafür. Einmal lässt sich pro Thread ein Random-Objekt generieren, sodass es im Code der Random-Klasse dann keine Konkurrenzsituation geben kann. Aber optimal ist das noch nicht, denn der Programmcode der Random-Klasse ist auf diese Nebenläufigkeit vorbereitet, und besser wäre ein schlankerer Programmcode, der für eine single-threaded Abarbeitung optimiert ist. Und hier kommt die unter Java 7 eingeführte Klasse java.util.concurrent.ThreadLocalRandom ins Spiel. Sie ist eine Unterklasse von Random und überschreibt die nextXXX()-Methoden so, dass es keine Synchronisation gibt; der Code ist dementsprechend schnell. Um etwa Zufallszahlen zwischen 1 und 10 zu erzeugen, schreiben wir mit der neuen Klasse:

Listing 2.43: com/tutego/insel/thread/ThreadLocalRandomDemo.java

ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
System.out.println( threadLocalRandom.nextInt( 1, 10 + 1 ) );
System.out.println( threadLocalRandom.nextInt( 1, 10 + 1 ) );

Rheinwerk Computing - Zum Seitenanfang

2.9.4 ThreadLocal bei der Performance-OptimierungZur vorigen Überschrift

ThreadLocal ist eine schöne Klasse zur Performance-Optimierung. Sollen zum Beispiel zwei Threads große Datenmengen über einen zentralen Puffer lesen, müssten sie an diesem Puffer synchronisiert werden. Mit ThreadLocal kann je ein Puffer beim Thread gespeichert sein, und die Synchronisation kann entfallen. Da es im Allgemeinen wenige Threads gibt, ist die Anzahl paralleler Puffer klein. Performance-Interessierte können einen Blick auf StringCoding werfen, wie sie ThreadLocal beim Caching von (De)Kodierer-Objekten nutzt. Die Klasse String nutzt StringCoding bei der Byte/Zeichen-Kodierung. Verschweigen dürfen wir aber nicht, dass der Zugriff auch etwas kostet, sodass ein kleiner synchronisierter Bereich durchaus schneller sein kann.

Dazu noch ein Punkt zum Caching: Wir hatten gesagt, dass sich gecachte Objekte sehr gut in einem ThreadLocal ablegen lassen, insbesondere dann, wenn diese Objekte teuer im Aufbau sind. Hier müssen wir aber vorsichtig sein. Ein Szenario: Ein Server nutzt 100 Threads für Anfragen. Es kommt eine Anfrage herein, ein Thread übernimmt sie und setzt ein zu cachendes Objekt in den ThreadLocal. Das Dumme ist, dass die nächste Anfrage aber nicht wieder bei diesem Thread landen muss, denn es gibt ja 99 andere Threads, und die haben den gecachten Wert nicht im ThreadLocal. Also machen die das Gleiche, und es endet nach einer Zeit damit, dass alle 100 Threads dieses Objekt in einem lokalen Cache haben. Diese Herangehensweise kostet viel Speicher, kann aber intelligent sein, da so eine Synchronisation unnötig wird. Ist Speicher satt vorhanden und sind die Objekte klein und nicht thread-sicher, ist das eine gute Philosophie, ansonsten ist vielleicht doch eine zentrale Stelle für die Objekte eine speicherschonendere Strategie.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.

>> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Java 7 – Mehr als eine Insel
Java 7 – Mehr als eine Insel
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Java SE Bibliotheken






 Java SE Bibliotheken


Zum Katalog: Professionell entwickeln mit Java EE 7






 Professionell
 entwickeln mit
 Java EE 7


Zum Katalog: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Katalog: Einstieg in Eclipse






 Einstieg in Eclipse


Zum Katalog: Einstieg in Java






 Einstieg in Java


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 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.


[Rheinwerk Computing]

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de