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

Inhaltsverzeichnis
Vorwort
1 Java ist auch eine Sprache
2 Sprachbeschreibung
3 Klassen und Objekte
4 Der Umgang mit Zeichenketten
5 Mathematisches
6 Eigene Klassen schreiben
7 Angewandte Objektorientierung
8 Exceptions
9 Die Funktionsbibliothek
10 Threads und nebenläufige Programmierung
11 Raum und Zeit
12 Datenstrukturen und Algorithmen
13 Dateien und Datenströme
14 Die eXtensible Markup Language (XML)
15 Grafische Oberflächen mit Swing
16 Grafikprogrammierung
17 Netzwerkprogrammierung
18 Verteilte Programmierung mit RMI und Web-Services
19 JavaServer Pages und Servlets
20 Applets
21 Midlets und die Java ME
22 Datenbankmanagement mit JDBC
23 Reflection und Annotationen
24 Logging und Monitoring
25 Sicherheitskonzepte
26 Java Native Interface (JNI)
27 Dienstprogramme für die Java-Umgebung
A Die Begleit-DVD
Stichwort

Download:
- ZIP, ca. 12,5 MB
Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Java ist auch eine Insel von Christian Ullenboom
Programmieren mit der Java Standard Edition Version 6
Buch: Java ist auch eine Insel

Java ist auch eine Insel
7., aktualisierte Auflage
geb., mit DVD (November 2007)
1.492 S., 49,90 Euro
Galileo Computing
ISBN 978-3-8362-1146-8
Pfeil 10 Threads und nebenläufige Programmierung
Pfeil 10.1 Nebenläufigkeit
Pfeil 10.1.1 Threads und Prozesse
Pfeil 10.1.2 Wie parallele Programme die Geschwindigkeit steigern können
Pfeil 10.1.3 Was Java für Nebenläufigkeit alles bietet
Pfeil 10.2 Threads erzeugen
Pfeil 10.2.1 Threads über die Schnittstelle Runnable implementieren
Pfeil 10.2.2 Thread mit Runnable starten
Pfeil 10.2.3 Der Name eines Threads
Pfeil 10.2.4 Die Klasse Thread erweitern
Pfeil 10.2.5 Wer bin ich?
Pfeil 10.3 Die Zustände eines Threads
Pfeil 10.3.1 Threads schlafen
Pfeil 10.3.2 Mit yield() auf Rechenzeit verzichten
Pfeil 10.3.3 Das Ende eines Threads
Pfeil 10.3.4 UncaughtExceptionHandler für unbehandelte Ausnahmen
Pfeil 10.3.5 Einen Thread höflich mit Interrupt beenden
Pfeil 10.3.6 Der stop() von außen und die Rettung mit ThreadDeath
Pfeil 10.3.7 Ein Rendezvous mit join()
Pfeil 10.3.8 Barrier und Austausch mit Exchanger
Pfeil 10.3.9 Arbeit niederlegen und wieder aufnehmen
Pfeil 10.3.10 Priorität
Pfeil 10.3.11 Der Thread ist ein Dämon
Pfeil 10.4 Der Ausführer (Executor) kommt
Pfeil 10.4.1 Die Schnittstelle Executor
Pfeil 10.4.2 Die Thread-Pools
Pfeil 10.4.3 Threads mit Rückgabe über Callable
Pfeil 10.4.4 Mehrere Callable abarbeiten
Pfeil 10.4.5 Mit ScheduledExecutorService wiederholende Ausgaben und Zeitsteuerungen
Pfeil 10.5 Synchronisation über kritische Abschnitte
Pfeil 10.5.1 Gemeinsam genutzte Daten
Pfeil 10.5.2 Probleme beim gemeinsamen Zugriff und kritische Abschnitte
Pfeil 10.5.3 Punkte parallel initialisieren
Pfeil 10.5.4 i++ sieht atomar aus, ist es aber nicht
Pfeil 10.5.5 Kritische Abschnitte schützen
Pfeil 10.5.6 Schützen mit ReentrantLock
Pfeil 10.5.7 Synchronisieren mit synchronized
Pfeil 10.5.8 Synchronized-Methoden der Klasse StringBuffer
Pfeil 10.5.9 Mit synchronized synchronisierte Blöcke
Pfeil 10.5.10 Dann machen wir doch gleich alles synchronisiert!
Pfeil 10.5.11 Lock-Freigabe im Fall von Exceptions
Pfeil 10.5.12 Mit synchronized nachträglich synchronisieren
Pfeil 10.5.13 Monitore sind reentrant – gut für die Geschwindigkeit
Pfeil 10.5.14 Synchronisierte Methodenaufrufe zusammenfassen
Pfeil 10.5.15 Deadlocks
Pfeil 10.6 Synchronisation über Warten und Benachrichtigen
Pfeil 10.6.1 Die Schnittstelle Condition
Pfeil 10.6.2 Beispiel: Erzeuger-Verbraucher-Programm
Pfeil 10.6.3 Warten mit wait() und Aufwecken mit notify()
Pfeil 10.6.4 Falls der Lock fehlt: IllegalMonitorStateException
Pfeil 10.6.5 Semaphor
Pfeil 10.7 Atomare Operationen und frische Werte mit volatile
Pfeil 10.7.1 Der Modifizierer volatile bei Objekt-/Klassenvariablen
Pfeil 10.7.2 Das Paket java.util.concurrent.atomic
Pfeil 10.8 Mit dem Thread verbundene Variablen
Pfeil 10.8.1 ThreadLocal
Pfeil 10.8.2 InheritableThreadLocal
Pfeil 10.9 Gruppen von Threads in einer Thread-Gruppe
Pfeil 10.9.1 Aktive Threads in der Umgebung
Pfeil 10.9.2 Etwas über die aktuelle Thread-Gruppe herausfinden
Pfeil 10.9.3 Threads in einer Thread-Gruppe anlegen
Pfeil 10.9.4 Methoden von Thread und ThreadGroup im Vergleich
Pfeil 10.10 Zeitgesteuerte Abläufe
Pfeil 10.10.1 Die Klassen Timer und TimerTask
Pfeil 10.10.2 Job-Scheduler Quartz
Pfeil 10.11 Einen Abbruch der virtuellen Maschine erkennen
Pfeil 10.12 Zum Weiterlesen


Galileo Computing - Zum Seitenanfang

10.2 Threads erzeugen Zur nächsten ÜberschriftZur vorigen Überschrift

Die folgenden Abschnitte verdeutlichen, wie der nebenläufige Programmcode in einen Runnable verpackt und dem Thread zur Ausführung vorgelegt wird.


Galileo Computing - Zum Seitenanfang

10.2.1 Threads über die Schnittstelle Runnable implementieren Zur nächsten ÜberschriftZur vorigen Überschrift

Damit der Thread weiß, was er ausführen soll, müssen wir ihm Anweisungsfolgen geben. Diese werden in einem Befehlsobjekt vom Typ Runnable verpackt und dem Thread übergeben. Wird der Thread gestartet, arbeitet er die Programmzeilen aus dem Befehlsobjekt parallel zum restlichen Programmcode ab. Die Schnittstelle Runnable ist schmal und schreibt nur eine run()-Methode vor.


interface java.lang.Runnable

  • void run() Diese Methode enthält den parallel auszuführenden Programmcode.

Wir wollen zwei Threads angeben, wobei einer zwanzigmal das aktuelle Datum und die Uhrzeit ausgibt und der andere einfach eine Zahl.

Listing 10.1 com/tutego/insel/thread/DateCommand.java

package com.tutego.insel.thread; 
 
public class DateCommand implements Runnable 
{ 
  @Override public void run() 
  { 
    for ( int i = 0; i < 20; i++ ) 
      System.out.println( new java.util.Date() ); 
  } 
}

Listing 10.2 com/tutego/insel/thread/CounterCommand.java

package com.tutego.insel.thread; 
 
class CounterCommand implements Runnable 
{ 
  @Override public void run() 
  { 
    for ( int i = 0; i < 20; i++ ) 
      System.out.println( i ); 
  } 
}

Unser parallel auszuführender Programmcode in run() besteht aus einer Schleife, die in einem Fall ein aktuelles Date-Objekt ausgibt und im anderen Fall einen Schleifenzähler.


Galileo Computing - Zum Seitenanfang

10.2.2 Thread mit Runnable starten Zur nächsten ÜberschriftZur vorigen Überschrift

Nun reicht es nicht aus, einfach die run()-Methode einer Klasse direkt aufzurufen. Würden wir dies tun, dann wäre nichts nebenläufig, sondern wir würden einfach eine Methode sequenziell ausführen. Damit der Programmcode parallel zur Applikation läuft, müssen wir ein Thread-Objekt mit dem Runnable verbinden und dann den Thread explizit starten. Dazu übergeben wir dem Konstruktor der Klasse Thread eine Referenz auf das Runnable-Objekt und rufen start() auf. Nachdem start() für den Thread eine Ablaufumgebung geschaffen hat, ruft es intern selbstständig die Methode run() genau einmal auf. Läuft der Thread schon, so löst ein zweiter Aufruf der start()-Methode eine IllegalThreadStateException aus.

Listing 10.3 com/tutego/insel/thread/FirstThread.java, main()

Thread t1 = new Thread( new DateCommand() ); 
t1.start(); 
 
Thread t2 = new Thread( new CounterCommand() ); 
t2.start();

Beim Starten des Programms erfolgt eine Ausgabe auf dem Bildschirm, die in etwa so aussehen kann:

Tue Aug 21 16:59:58 CEST 2007 
0 
1 
2 
3 
4 
5 
6 
7 
8 
9 
Tue Aug 21 16:59:58 CEST 2007 
10 
...

Deutlich ist die Verzahnung der beiden Threads zu erkennen. Was allerdings auf den ersten Blick etwas merkwürdig wirkt, ist die erste Zeile des Datum-Threads und viele weitere Zeilen des Zähl-Threads. Dies hat jedoch nichts zu bedeuten und zeigt deutlich den Nichtdeterminismus bei Threads. Interpretiert werden kann dies jedoch durch die unterschiedlichen Laufzeiten, die für die Datums- und Zeitausgabe nötig sind.


class java.lang.Thread 
implements Runnable

  • Thread( Runnable target ) Erzeugt einen neuen Thread mit einem Runnable, das den parallel auszuführenden Programmcode vorgibt.
  • void start() Ein neuer Thread – neben dem die Methode aufrufenden Thread – wird gestartet. Der neue Thread führt die run()-Methode nebenläufig aus. Jeder Thread kann nur einmal gestartet werden.

Automatisches Starten während der Erzeugung

Erst der Aufruf der start()-Methode lässt den Thread ablaufen. Etwas eleganter ist der Weg, dass das Objekt seinen eigenen Thread verwaltet, der im Konstruktor gestartet wird. Dann muss dort ein Thread-Objekt für die Runnable-Umgebung angelegt und die start()-Methode ausgeführt werden. An einem Beispiel ausprobiert, kann das so aussehen: Eine Klasse DateThreadAutoStart soll beim Anlegen automatisch einen Thread starten. Dazu initialisiert der Standard-Konstruktor von DateThreadAutoStart ein neues Thread-Objekt mit der this-Referenz als Argument.

Listing 10.4 com/tutego/insel/thread/DateThreadAutoStart.java, DateThreadAutoStart

class DateThreadAutoStart implements Runnable 
{ 
  DateThreadAutoStart() 
  { 
    new Thread( this ).start(); 
  } 
 
  @Override public void run() 
  { 
    for ( int i = 0; i < 20; i++ ) 
      System.out.println( new Date() ); 
  } 
}

Hinweis Hinweis Wenn ein Thread im Konstruktor gebildet wird, sollte die Arbeitsweise bei der Vererbung beachtet werden. Nehmen wir an, eine Klasse leitet von einer anderen Klasse ab, die im Konstruktor einen Thread startet. Bildet die Applikation ein Exemplar der Unterklasse, so werden bei der Bildung des Objekts immer erst die Konstruktoren der Oberklasse aufgerufen. Dies hat zur Folge, dass der Thread schon läuft, auch wenn das Objekt noch nicht ganz gebaut ist. Die Erzeugung ist erst abgeschlossen, wenn nach dem Aufruf der Konstruktoren der Oberklassen der eigene Konstruktor vollständig abgearbeitet wurde.



Galileo Computing - Zum Seitenanfang

10.2.3 Der Name eines Threads Zur nächsten ÜberschriftZur vorigen Überschrift

Ein Thread hat eine ganze Menge Eigenschaften – wie einen Zustand, eine Priorität und auch einen Namen. Er kann mit setName() gesetzt und mit getName() erfragt werden.


class java.lang.Thread 
implements Runnable

  • Thread( Runnable target, String name ) Erzeugt ein neues Thread-Objekt mit einem Runnable und setzt den Namen.
  • final String getName() Liefert den Namen des Threads. Der Name wird im Konstruktor angegeben oder mit setName() zugewiesen. Standardmäßig ist der Name »Thread-x«, wobei x eine eindeutige Nummer ist.
  • final void setName( String name ) Ändert den Namen des Threads.

Galileo Computing - Zum Seitenanfang

10.2.4 Die Klasse Thread erweitern Zur nächsten ÜberschriftZur vorigen Überschrift

Da die Klasse Thread selbst die Schnittstelle Runnable implementiert und die run()-Methode mit leerem Programmcode bereitstellt, können wir auch Thread erweitern, wenn wir eigene parallele Aktivitäten programmieren wollen.

Listing 10.5 com/tutego/insel/thread/DateThread.java, DateThread

public class DateThread extends Thread 
{ 
  @Override public void run() 
  { 
    for ( int i = 0; i < 20; i++ ) 
      System.out.println( new Date() ); 
  } 
}

Dann müssen wir kein Runnable-Exemplar mehr in den Konstruktor einfügen, denn wenn unsere Klasse eine Unterklasse von Thread ist, reicht ein Aufruf der geerbten Methode start(). Danach arbeitet das Programm direkt weiter, führt also kurze Zeit später die nächste Anweisung hinter start() aus.

Thread t = new DateThread(); 
t.start();

Es geht auch ohne Zwischenspeicherung der Objektreferenz:

new DateThread().start();

Die Methode start() kann von uns auch überschrieben werden, was aber nur selten sinnvoll beziehungsweise nötig ist. Wir müssen dann darauf achten, super.start() aufzurufen, damit der Thread wirklich startet.

Damit wir als Thread-Benutzer nicht erst die start()-Methode aufrufen müssen, kann ein Thread sich auch wieder selbst starten. Der Konstruktor enthält den Methodenaufruf start().

class DateThread extends Thread 
{ 
  DateThread() 
  { 
    start(); 
  } 
  // ...  der Rest bleibt ... 
}

class java.lang.Thread 
implements Runnable

  • void run() Diese Methode enthält den parallel auszuführenden Programmcode.
  • Thread( String name ) Erzeugt ein neues Thread-Objekt und setzt den Namen. Sinnvoll bei Unterklassen, die den Konstruktor über super(name) aufrufen.

Was passiert, wenn wir run() an Stelle von start() aufrufen?

Ein Programmierfehler, der hin und wieder passiert, ist folgender: Statt start() rufen wir aus Versehen run() auf. Was geschieht? Fast genau das Gleiche wie bei start(), nur mit dem Unterschied, dass die Objektmethode run() nicht parallel zum übrigen Programm abgearbeitet wird. Der aktuelle Thread bearbeitet die run()-Methode sequenziell, bis sie zu Ende ist und die Anweisungen nach dem Aufruf an die Reihe kommen. Der Fehler fällt nicht immer direkt auf, denn die Aktionen in run() finden ja statt – nur nicht nebenläufig.

Erweitern von Thread oder Implementieren von Runnable?

Die beste Idee wäre, Runnable-Objekte zu bauen, die dann dem Thread übergeben werden. Befehlsobjekte dieser Art sind recht flexibel, da die einfachen Runnable-Objekte leicht übergeben und sogar von Threads aus einem Thread-Pool ausgeführt werden können. Ein Nachteil der Thread-Erweiterung ist, dass die Einfachvererbung störend sein kann; erbt eine Klasse von Thread, ist die Erweiterung schon »aufgebraucht«. Doch ob eine Klasse Runnable implementiert oder Thread erweitert, eines bleibt: eine neue Klasse.


Galileo Computing - Zum Seitenanfang

10.2.5 Wer bin ich? topZur vorigen Überschrift

Eine Erweiterung der Klasse Thread hat den Vorteil, dass geerbte Methoden wie getName() sofort genutzt werden können. Wenn wir Runnable implementieren, genießen wir diesen Vorteil nicht.

Die Klasse Thread liefert mit der Klassenmethode currentThread() die Objektreferenz für das Thread-Exemplar, das diese Anweisung gerade ausführt. Auf diese Weise lassen sich nicht-statische Thread-Methoden wie getName() verwenden.


Beispiel Beispiel Gib die aktuelle Priorität des laufenden Threads aus:

System.out.println( Thread.currentThread().getPriority() );   // 5

Falls es in einer Schleife wiederholten Zugriff auf Thread.currentThread() gibt, sollte das Ergebnis zwischengespeichert werden, denn der Aufruf der nativen Methoden ist nicht ganz billig.


class java.lang.Thread 
implements Runnable

  • static Thread currentThread() Liefert den Thread, der das laufende Programmstück ausführt.


Ihr Kommentar

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






<< zurück



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