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 8 und Java 7
2 Fortgeschrittene String-Verarbeitung
3 Threads und nebenläufige Programmierung
4 Datenstrukturen und Algorithmen
5 Raum und Zeit
6 Dateien, Verzeichnisse und Dateizugriffe
7 Datenströme
8 Die eXtensible Markup Language (XML)
9 Dateiformate
10 Grafische Oberflächen mit Swing
11 Grafikprogrammierung
12 JavaFX
13 Netzwerkprogrammierung
14 Verteilte Programmierung mit RMI
15 RESTful und SOAP-Web-Services
16 Technologien für die Infrastruktur
17 Typen, Reflection und Annotationen
18 Dynamische Übersetzung und Skriptsprachen
19 Logging und Monitoring
20 Sicherheitskonzepte
21 Datenbankmanagement mit JDBC
22 Java Native Interface (JNI)
23 Dienstprogramme für die Java-Umgebung
Stichwortverzeichnis

Jetzt Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java SE 8 Standard-Bibliothek von Christian Ullenboom
Das Handbuch für Java-Entwickler
Buch: Java SE 8 Standard-Bibliothek

Java SE 8 Standard-Bibliothek
Pfeil 3 Threads und nebenläufige Programmierung
Pfeil 3.1 Threads erzeugen
Pfeil 3.1.1 Threads über die Schnittstelle Runnable implementieren
Pfeil 3.1.2 Thread mit Runnable starten
Pfeil 3.1.3 Die Klasse Thread erweitern
Pfeil 3.2 Thread-Eigenschaften und -Zustände
Pfeil 3.2.1 Der Name eines Threads
Pfeil 3.2.2 Wer bin ich?
Pfeil 3.2.3 Die Zustände eines Threads *
Pfeil 3.2.4 Schläfer gesucht
Pfeil 3.2.5 Mit yield() auf Rechenzeit verzichten
Pfeil 3.2.6 Der Thread als Dämon
Pfeil 3.2.7 Freiheit für den Thread – das Ende
Pfeil 3.2.8 Einen Thread höflich mit Interrupt beenden
Pfeil 3.2.9 UncaughtExceptionHandler für unbehandelte Ausnahmen
Pfeil 3.2.10 Der stop() von außen und die Rettung mit ThreadDeath *
Pfeil 3.2.11 Ein Rendezvous mit join(…) *
Pfeil 3.2.12 Arbeit niederlegen und wieder aufnehmen *
Pfeil 3.2.13 Priorität *
Pfeil 3.3 Der Ausführer (Executor) kommt
Pfeil 3.3.1 Die Schnittstelle Executor
Pfeil 3.3.2 Glücklich in der Gruppe – die Thread-Pools
Pfeil 3.3.3 Threads mit Rückgabe über Callable
Pfeil 3.3.4 Mehrere Callable abarbeiten
Pfeil 3.3.5 ScheduledExecutorService für wiederholende Ausgaben und Zeitsteuerungen nutzen
Pfeil 3.4 Synchronisation über kritische Abschnitte
Pfeil 3.4.1 Gemeinsam genutzte Daten
Pfeil 3.4.2 Probleme beim gemeinsamen Zugriff und kritische Abschnitte
Pfeil 3.4.3 Punkte nebenläufig initialisieren
Pfeil 3.4.4 i++ sieht atomar aus, ist es aber nicht *
Pfeil 3.4.5 Kritische Abschnitte schützen
Pfeil 3.4.6 Kritische Abschnitte mit ReentrantLock schützen
Pfeil 3.4.7 Synchronisieren mit synchronized
Pfeil 3.4.8 Synchronized-Methoden der Klasse StringBuffer *
Pfeil 3.4.9 Mit synchronized synchronisierte Blöcke
Pfeil 3.4.10 Dann machen wir doch gleich alles synchronisiert!
Pfeil 3.4.11 Lock-Freigabe im Fall von Exceptions
Pfeil 3.4.12 Deadlocks
Pfeil 3.4.13 Mit synchronized nachträglich synchronisieren *
Pfeil 3.4.14 Monitore sind reentrant – gut für die Geschwindigkeit *
Pfeil 3.4.15 Synchronisierte Methodenaufrufe zusammenfassen *
Pfeil 3.5 Synchronisation über Warten und Benachrichtigen
Pfeil 3.5.1 Die Schnittstelle Condition
Pfeil 3.5.2 It’s Disco-Time *
Pfeil 3.5.3 Warten mit wait(…) und Aufwecken mit notify()/notifyAll() *
Pfeil 3.5.4 Falls der Lock fehlt – IllegalMonitorStateException *
Pfeil 3.6 Datensynchronisation durch besondere Concurrency-Klassen *
Pfeil 3.6.1 Semaphor
Pfeil 3.6.2 Barrier und Austausch
Pfeil 3.6.3 Stop and go mit Exchanger
Pfeil 3.7 Atomare Operationen und frische Werte mit volatile *
Pfeil 3.7.1 Der Modifizierer volatile bei Objekt-/Klassenvariablen
Pfeil 3.7.2 Das Paket java.util.concurrent.atomic
Pfeil 3.8 Teile und herrsche mit Fork und Join *
Pfeil 3.8.1 Algorithmendesign per »teile und herrsche«
Pfeil 3.8.2 Nebenläufiges Lösen von D&C-Algorithmen
Pfeil 3.8.3 Fork und Join
Pfeil 3.9 CompletionStage und CompletableFuture *
Pfeil 3.10 Mit dem Thread verbundene Variablen *
Pfeil 3.10.1 ThreadLocal
Pfeil 3.10.2 InheritableThreadLocal
Pfeil 3.10.3 ThreadLocalRandom als schneller nebenläufiger Zufallszahlengenerator
Pfeil 3.10.4 ThreadLocal bei der Performance-Optimierung
Pfeil 3.11 Threads in einer Thread-Gruppe *
Pfeil 3.11.1 Aktive Threads in der Umgebung
Pfeil 3.11.2 Etwas über die aktuelle Thread-Gruppe herausfinden
Pfeil 3.11.3 Threads in einer Thread-Gruppe anlegen
Pfeil 3.11.4 Methoden von Thread und ThreadGroup im Vergleich
Pfeil 3.12 Zeitgesteuerte Abläufe
Pfeil 3.12.1 Die Typen Timer und TimerTask
Pfeil 3.12.2 Job-Scheduler Quartz
Pfeil 3.13 Einen Abbruch der virtuellen Maschine erkennen
Pfeil 3.13.1 Shutdown-Hook
Pfeil 3.13.2 Signale
Pfeil 3.14 Zum Weiterlesen
 
Zum Seitenanfang

3.10Mit dem Thread verbundene Variablen * Zur vorigen ÜberschriftZur 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 Kontext-Informationen ablegen kann. Natürlich könnte jedem Runnable-Exemplar so eine lokale Datenstruktur gegeben werden, doch Java bringt von Haus etwas Besseres mit. Das ist praktisch, denn so sind Runnable und die vom Thread referenzierte Datenstruktur unabhängig voneinander.

 
Zum Seitenanfang

3.10.1ThreadLocal Zur vorigen ÜberschriftZur nächsten Überschrift

Java bietet mit der Klasse java.lang.ThreadLocal eine Implementierung für threadlokale Variablen (engl. thread-local storage[TLS]) an, sodass mit dem laufenden Thread ein Objekt, zum Beispiel eine Datenstruktur, assoziiert werden kann. Dieses Objekt ist dann aus dem Runnable-Objekt heraus referenzierbar. Sie sind ein wenig wie globale Variablen im Speicherraum der Threads.

class java.lang.ThreadLocal<T>
  • ThreadLocal()
    Erzeugt eine neue threadlokale 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 threadlokalen Variablen, um etwa Speicher freizumachen. Ein anschließendes get() liefert wieder den Wert aus initialValue().

  • static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)
    Erzeugt über die Fabrikmethode ein neues ThreadLocal-Objekt, wobei der angegebene Supplier den Initialwert liefert. Neu in Java 8.

Zählen mit threadlokalen 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 threadlokale Variable ist, die ThreadLocal verwaltet. Drei Threads sollen das gleiche Runnable abarbeiten: zwei neue Threads und der aktuell ausführende main-Thread:

Listing 3.43com/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

ThreadLocal bietet eine schöne Lösung, um Synchronisation zu vermeiden, indem jeder Thread einfach sein eigenes Objekt bekommt. Folglich muss sich ein Thread nicht mit anderen Threads um den Zugriff streiten. Natürlich funktioniert das nur dann, wenn diese geteilten Objekte wirklich unabhängig sind. In einem Java EE-Server werden üblicherweise die Transaktionsinformationen in threadlokalen Variablen gehalten.

Entwicklungsumgebungen

Die IDE IntelliJ bietet die komfortable Möglichkeit, statische referenzierte nicht threadsichere 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 threadlokalen Variablen. Damit im ThreadLocal vergessene Werte nicht zum Problem werden, müssen 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 threadlokale 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.

 
Zum Seitenanfang

3.10.2InheritableThreadLocal Zur vorigen ÜberschriftZur nächsten Ü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 3.44com/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<>();
private static final InheritableThreadLocal<String> mem =
new InheritableThreadLocal<>();
@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.

 
Zum Seitenanfang

3.10.3ThreadLocalRandom als schneller nebenläufiger Zufallszahlengenerator Zur vorigen ÜberschriftZur nächsten Ü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. Math ist also eine Klasse mit Zustand, und Zustandsverwaltung ist bei Multithreaded Anwendungen immer etwas speziell.

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 bei nur einem Thread wäre ein schlankerer Programmcode besser, der für eine Single-Threaded Abarbeitung optimiert ist. Und hier kommt die Klasse java.util.concurrent.ThreadLocalRandom ins Spiel. Sie ist eine Unterklasse von Random und überschreibt die eigentliche Generatormethode next(int)-Methode so, dass es keine Synchronisation gibt; die Ausführung in einer Multithreaded Umgebung ist dementsprechend schnell.

[zB]Beispiel

Erzeuge Zufallszahlen zwischen 1 und 10:

Listing 3.45com/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 ) );

Die Variable threadLocalRandom kann problemlos zwischen verschiedenen Threads »geteilt« werden.

Die Klasse ThreadLocalRandom erbt alle Methoden von Random und überschreibt etwa die neuen Methoden aus Java 8, die einen Stream von Zufallszahlen liefern. Des Weiteren kommen einige neue Methoden hinzu, um etwa Zufallszahlen in einem gewissen Bereich zu generieren – das fehlt in Random. Ein neuer Seed kann nicht gesetzt werden, ThreadLocalRandom überschreibt setSeed(long) so, dass eine UnsupportedOperationException ausgelöst wird.

class java.util.concurrent.ThreadLocalRandom
extends Random
  • static ThreadLocalRandom current()
    Liefert das aktuelle ThreadLocalRandom-Objekt.

  • void setSeed(long seed)
    Nicht unterstützt, löst UnsupportedOperationException aus.

  • double nextDouble(double n)

  • double nextDouble(double least, double bound)

  • double nextGaussian()

  • int nextInt(int least, int bound)

  • long nextLong(long n)

  • long nextLong(long least, long bound)
    Liefert Zufallszahl und aktualisiert den Seed.

  • DoubleStream doubles()

  • IntStream ints()

  • LongStream longs()

  • DoubleStream gaussians()
    Liefert einen Stream von Daten.

  • protected int next(int bits)
    Liefert die nächste Zufallszahl, eine interne Methode, die ThreadLocalRandom aus Random überschreibt und protected belässt.

 
Zum Seitenanfang

3.10.4ThreadLocal bei der Performance-Optimierung Zur vorigen ÜberschriftZur nächsten Ü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 nötiger 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 threadsicher, ist das eine gute Philosophie, ansonsten ist vielleicht doch eine zentrale Stelle für die Objekte eine speicherschonendere Strategie.

 


Ihre Meinung

Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.

<< zurück
 Zum Rheinwerk-Shop
Zum Rheinwerk-Shop: Java SE 8 Standard-Bibliothek Java SE 8 Standard-Bibliothek
Jetzt Buch bestellen

 Buchempfehlungen
Zum Rheinwerk-Shop: Java ist auch eine Insel
Java ist auch eine Insel


Zum Rheinwerk-Shop: Professionell entwickeln mit Java EE 8
Professionell entwickeln mit Java EE 8


Zum Rheinwerk-Shop: Besser coden
Besser coden


Zum Rheinwerk-Shop: Entwurfsmuster
Entwurfsmuster


Zum Rheinwerk-Shop: IT-Projektmanagement
IT-Projektmanagement


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
InfoInfo

 
 


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

 
Nutzungsbestimmungen | Datenschutz | Impressum

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

Cookie-Einstellungen ändern