Rheinwerk Computing < openbook > Rheinwerk 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 Eigene Klassen schreiben
6 Exceptions
7 Generics<T>
8 Äußere.innere Klassen
9 Besondere Klassen der Java SE
10 Architektur, Design und angewandte Objektorientierung
11 Die Klassenbibliothek
12 Bits und Bytes und Mathematisches
13 Datenstrukturen und Algorithmen
14 Threads und nebenläufige Programmierung
15 Raum und Zeit
16 Dateien, Verzeichnisse und Dateizugriffe
17 Datenströme
18 Die eXtensible Markup Language (XML)
19 Grafische Oberflächen mit Swing
20 Grafikprogrammierung
21 Netzwerkprogrammierung
22 Verteilte Programmierung mit RMI
23 JavaServer Pages und Servlets
24 Datenbankmanagement mit JDBC
25 Reflection und Annotationen
26 Dienstprogramme für die Java-Umgebung
A Die Begleit-DVD
Stichwort
Ihre Meinung?

Spacer
 <<   zurück
Java ist auch eine Insel von Christian Ullenboom
Das umfassende Handbuch
Buch: Java ist auch eine Insel

Java ist auch eine Insel
geb., mit DVD
1482 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1506-0
Pfeil 14 Threads und nebenläufige Programmierung
  Pfeil 14.1 Nebenläufigkeit
    Pfeil 14.1.1 Threads und Prozesse
    Pfeil 14.1.2 Wie parallele Programme die Geschwindigkeit steigern können
    Pfeil 14.1.3 Was Java für Nebenläufigkeit alles bietet
  Pfeil 14.2 Threads erzeugen
    Pfeil 14.2.1 Threads über die Schnittstelle Runnable implementieren
    Pfeil 14.2.2 Thread mit Runnable starten
    Pfeil 14.2.3 Die Klasse Thread erweitern
  Pfeil 14.3 Thread-Eigenschaften und -Zustände
    Pfeil 14.3.1 Der Name eines Threads
    Pfeil 14.3.2 Wer bin ich?
    Pfeil 14.3.3 Die Zustände eines Threads *
    Pfeil 14.3.4 Schläfer gesucht
    Pfeil 14.3.5 Mit »yield()« auf Rechenzeit verzichten
    Pfeil 14.3.6 Der Thread als Dämon
    Pfeil 14.3.7 Das Ende eines Threads
    Pfeil 14.3.8 Einen Thread höflich mit Interrupt beenden
    Pfeil 14.3.9 »UncaughtExceptionHandler« für unbehandelte Ausnahmen
    Pfeil 14.3.10 Der »stop()« von außen und die Rettung mit ThreadDeath *
    Pfeil 14.3.11 Ein Rendezvous mit »join()« *
    Pfeil 14.3.12 Arbeit niederlegen und wieder aufnehmen *
    Pfeil 14.3.13 Priorität *
  Pfeil 14.4 Der Ausführer (Executor) kommt
    Pfeil 14.4.1 Die Schnittstelle »Executor«
    Pfeil 14.4.2 Die Thread-Pools
    Pfeil 14.4.3 Threads mit Rückgabe über Callable
    Pfeil 14.4.4 Mehrere Callable abarbeiten
    Pfeil 14.4.5 Mit ScheduledExecutorService wiederholende Ausgaben und Zeitsteuerungen
  Pfeil 14.5 Synchronisation über kritische Abschnitte
    Pfeil 14.5.1 Gemeinsam genutzte Daten
    Pfeil 14.5.2 Probleme beim gemeinsamen Zugriff und kritische Abschnitte
    Pfeil 14.5.3 Punkte parallel initialisieren
    Pfeil 14.5.4 »i++« sieht atomar aus, ist es aber nicht *
    Pfeil 14.5.5 Kritische Abschnitte schützen
    Pfeil 14.5.6 Schützen mit ReentrantLock
    Pfeil 14.5.7 Synchronisieren mit »synchronized«
    Pfeil 14.5.8 Synchronized-Methoden der Klasse »StringBuffer« *
    Pfeil 14.5.9 Mit synchronized synchronisierte Blöcke
    Pfeil 14.5.10 Dann machen wir doch gleich alles synchronisiert!
    Pfeil 14.5.11 Lock-Freigabe im Fall von Exceptions
    Pfeil 14.5.12 Deadlocks
    Pfeil 14.5.13 Mit »synchronized« nachträglich synchronisieren *
    Pfeil 14.5.14 Monitore sind reentrant – gut für die Geschwindigkeit *
    Pfeil 14.5.15 Synchronisierte Methodenaufrufe zusammenfassen *
  Pfeil 14.6 Synchronisation über Warten und Benachrichtigen
    Pfeil 14.6.1 Die Schnittstelle »Condition«
    Pfeil 14.6.2 It’s Disco-Time *
    Pfeil 14.6.3 Warten mit »wait()« und Aufwecken mit »notify()« *
    Pfeil 14.6.4 Falls der Lock fehlt: IllegalMonitorStateException *
  Pfeil 14.7 Zeitgesteuerte Abläufe
    Pfeil 14.7.1 Die Klassen »Timer« und »TimerTask«
    Pfeil 14.7.2 Job-Scheduler Quartz
  Pfeil 14.8 Einen Abbruch der virtuellen Maschine erkennen
  Pfeil 14.9 Zum Weiterlesen


Rheinwerk Computing - Zum Seitenanfang

14.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.


Rheinwerk Computing - Zum Seitenanfang

14.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() Implementierende Klassen realisieren die Operation und setzen dort den parallel auszuführenden Programmcode rein.

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

Listing 14.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 14.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.


Rheinwerk Computing - Zum Seitenanfang

14.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, denn 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 14.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 [Nicht vorhersehbar, meint hier: Wann der Scheduler den Kontextwechsel vornimmt, ist unbekannt. ] 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.

Hinweis Wenn ein Thread im Konstruktor einer Runnable-Implementierung gestartet 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.



Rheinwerk Computing - Zum Seitenanfang

14.2.3 Die Klasse Thread erweitern  topZur 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 14.4  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 in Thread hat einen leeren Rumpf. Unterklassen überschreiben run(), sodass sie den parallel auszuführenden Programmcode enthält.
  • Thread( String name ) Erzeugt ein neues Thread-Objekt und setzt den Namen. Sinnvoll bei Unterklassen, die den Konstruktor über super(name) aufrufen.

»run(«) statt »start()« aufgerufen: Ja wo laufen sie denn?

Ein Programmierfehler, der Anfängern schnell unterläuft, ist folgender: Statt start() rufen sie aus Versehen run() auf dem Thread 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 eben 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.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
 <<   zurück
 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Java ist auch eine Insel






 Java ist auch
 eine Insel


Zum Katalog: Java SE Bibliotheken






 Java SE Bibliotheken


Zum Katalog: Professionell entwickeln mit Java EE 7






 Professionell
 entwickeln mit
 Java EE 7


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 2011
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