Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Java ist auch eine Insel von Christian Ullenboom
Programmieren für die Java 2-Plattform in der Version 5
Java ist auch eine Insel

Java ist auch eine Insel
5., akt. und erw. Auflage
1454 S., mit CD, 49,90 Euro
Galileo Computing
ISBN 3-89842-747-1
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 Thread mit Runnable starten
    gp 9.2.3 Der Name eines Threads
    gp 9.2.4 Die Klasse Thread erweitern
    gp 9.2.5 Wer bin ich?
  gp 9.3 Der Ausführer (Executor) kommt
    gp 9.3.1 Die Schnittstelle Executor
    gp 9.3.2 Die Thread-Pools
    gp 9.3.3 Threads mit Rückgabe über Callable
    gp 9.3.4 Mehrere Callable abarbeiten
    gp 9.3.5 Mit ScheduledExecutorService wiederholende Ausgaben und Zeitsteuerungen
  gp 9.4 Die Zustände eines Threads
    gp 9.4.1 Threads schlafen
    gp 9.4.2 Das Ende eines Threads
    gp 9.4.3 UncaughtExceptionHandler für unbehandelte Ausnahmen
    gp 9.4.4 Einen Thread höflich mit Interrupt beenden
    gp 9.4.5 Der stop() von außen und die Rettung mit ThreadDeath
    gp 9.4.6 Ein Rendezvous mit join() und Barrier sowie Austausch mit Exchanger
    gp 9.4.7 Mit yield() auf Rechenzeit verzichten
    gp 9.4.8 Arbeit niederlegen und wieder aufnehmen
    gp 9.4.9 Priorität
    gp 9.4.10 Der Thread ist ein Dämon
  gp 9.5 Synchronisation über kritische Abschnitte
    gp 9.5.1 Gemeinsam genutzte Daten
    gp 9.5.2 Probleme beim gemeinsamen Zugriff und kritische Abschnitte
    gp 9.5.3 Punkte parallel initialisieren
    gp 9.5.4 i++ sieht atomar aus, ist es aber nicht
    gp 9.5.5 Kritische Abschnitte schützen
    gp 9.5.6 Schützen mit ReentrantLock
    gp 9.5.7 Synchronisieren mit synchronized
    gp 9.5.8 Synchronized-Methoden der Klasse StringBuffer
    gp 9.5.9 Mit synchronized synchronisierte Blöcke
    gp 9.5.10 Look-Freigabe im Fall von Exceptions
    gp 9.5.11 Mit synchronized nachträglich synchronisieren
    gp 9.5.12 Monitore sind reentrant, gut für die Geschwindigkeit
    gp 9.5.13 Synchronisierte Methodenaufrufe zusammenfassen
    gp 9.5.14 Deadlocks
    gp 9.5.15 Erkennen von Deadlocks
  gp 9.6 Synchronisation über Warten und Benachrichtigen
    gp 9.6.1 Die Schnittstelle Condition
    gp 9.6.2 Beispiel Erzeuger-Verbraucher-Programm
    gp 9.6.3 Warten mit wait() und Aufwecken mit notify()
    gp 9.6.4 Falls der Lock fehlt: IllegalMonitorStateException
    gp 9.6.5 Semaphoren
  gp 9.7 Atomares und frische Werte mit volatile
    gp 9.7.1 Der Modifizierer volatile bei Objekt-/Klassenvariablen
    gp 9.7.2 Das Paket java.util.concurrent.atomic
  gp 9.8 Mit dem Thread verbundene Variablen
    gp 9.8.1 ThreadLocal
    gp 9.8.2 InheritableThreadLocal
  gp 9.9 Gruppen von Threads in einer Thread-Gruppe
    gp 9.9.1 Aktive Threads in der Umgebung
    gp 9.9.2 Etwas über die aktuelle Thread-Gruppe herausfinden
    gp 9.9.3 Threads in einer Thread-Gruppe anlegen
    gp 9.9.4 Methoden von Thread und ThreadGroup im Vergleich
  gp 9.10 Die Klassen Timer und TimerTask
    gp 9.10.1 Job-Scheduler Quartz
  gp 9.11 Einen Abbruch der virtuellen Maschine erkennen


Galileo Computing

9.8 Mit dem Thread verbundene Variabledowntop

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, im 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 bei genau einem Runnable-Objekt und beliebig vielen Threads müsste ein anderer Ort gefunden werden.


Galileo Computing

9.8.1 ThreadLocal  downtop

Die Lösung ist relativ einfach: Es müsste eine Datenstruktur geben, die jeden Current-Thread mit einem Objekt assoziiert. Diese Struktur muss dann im Runnable-Objekt referenzierbar sein. Java bietet mit der Klasse java.lang.ThreadLocal eine Implementierung für Thread lokale Variablen.


class java.lang.ThreadLocal<T>

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

Das folgende Beispiel soll ein Runnable definieren, das endlos zählt. Der Clue 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 9.26   ThreadLocalDemo.java

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; }
  };
    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:

main1
main2
main3
main4
main5
Thread-01
Thread-11
Thread-02
Thread-12
Thread-03
Thread-13

Galileo Computing

9.8.2 InheritableThreadLocal  toptop

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

Mit der Klasse InheritableThreadLocal, die 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 9.27   InheritableThreadLocalDemo.java

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 vom 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, wird merken, dass das Beispiel so nicht funktioniert.

 << zurück




Copyright © Galileo Press GmbH 2005
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, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de