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 Generics, innere Klassen
10 Die Klassenbibliothek
11 Threads und nebenläufige Programmierung
12 Datenstrukturen und Algorithmen
13 Raum und Zeit
14 Dateien und Datenströme
15 Die eXtensible Markup Language (XML)
16 Grafische Oberflächen mit Swing
17 Grafikprogrammierung
18 Netzwerkprogrammierung
19 Verteilte Programmierung mit RMI und Web–Services
20 JavaServer Pages und Servlets
21 Applets
22 Midlets und die Java ME
23 Datenbankmanagement mit JDBC
24 Reflection und Annotationen
25 Logging und Monitoring
26 Sicherheitskonzepte
27 Java Native Interface (JNI)
28 Dienstprogramme für die Java-Umgebung
Stichwort

Download:
- ZIP, ca. 14,1 MB
Buch bestellen
Ihre Meinung?

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

Java ist auch eine Insel (8. Aufl.)
8., aktual. Auflage, geb., mit DVD
1.475 S., 49,90 Euro
Galileo Computing
ISBN 978-3-8362-1371-4
Pfeil 11 Threads und nebenläufige Programmierung
Pfeil 11.1 Nebenläufigkeit
Pfeil 11.1.1 Threads und Prozesse
Pfeil 11.1.2 Wie parallele Programme die Geschwindigkeit steigern können
Pfeil 11.1.3 Was Java für Nebenläufigkeit alles bietet
Pfeil 11.2 Threads erzeugen
Pfeil 11.2.1 Threads über die Schnittstelle Runnable implementieren
Pfeil 11.2.2 Thread mit Runnable starten
Pfeil 11.2.3 Der Name eines Threads
Pfeil 11.2.4 Die Klasse Thread erweitern
Pfeil 11.2.5 Wer bin ich?
Pfeil 11.3 Die Zustände eines Threads
Pfeil 11.3.1 Threads schlafen
Pfeil 11.3.2 Mit yield() auf Rechenzeit verzichten
Pfeil 11.3.3 Das Ende eines Threads
Pfeil 11.3.4 UncaughtExceptionHandler für unbehandelte Ausnahmen
Pfeil 11.3.5 Einen Thread höflich mit Interrupt beenden
Pfeil 11.3.6 Der stop() von außen und die Rettung mit ThreadDeath
Pfeil 11.3.7 Ein Rendezvous mit join()
Pfeil 11.3.8 Barrier und Austausch mit Exchanger
Pfeil 11.3.9 Arbeit niederlegen und wieder aufnehmen
Pfeil 11.3.10 Priorität
Pfeil 11.3.11 Der Thread als Dämon
Pfeil 11.4 Der Ausführer (Executor) kommt
Pfeil 11.4.1 Die Schnittstelle Executor
Pfeil 11.4.2 Die Thread-Pools
Pfeil 11.4.3 Threads mit Rückgabe über Callable
Pfeil 11.4.4 Mehrere Callable abarbeiten
Pfeil 11.4.5 Mit ScheduledExecutorService wiederholende Ausgaben und Zeitsteuerungen
Pfeil 11.5 Synchronisation über kritische Abschnitte
Pfeil 11.5.1 Gemeinsam genutzte Daten
Pfeil 11.5.2 Probleme beim gemeinsamen Zugriff und kritische Abschnitte
Pfeil 11.5.3 Punkte parallel initialisieren
Pfeil 11.5.4 i++ sieht atomar aus, ist es aber nicht
Pfeil 11.5.5 Kritische Abschnitte schützen
Pfeil 11.5.6 Schützen mit ReentrantLock
Pfeil 11.5.7 Synchronisieren mit synchronized
Pfeil 11.5.8 Synchronized-Methoden der Klasse StringBuffer
Pfeil 11.5.9 Mit synchronized synchronisierte Blöcke
Pfeil 11.5.10 Dann machen wir doch gleich alles synchronisiert!
Pfeil 11.5.11 Lock-Freigabe im Fall von Exceptions
Pfeil 11.5.12 Mit synchronized nachträglich synchronisieren
Pfeil 11.5.13 Monitore sind reentrant – gut für die Geschwindigkeit
Pfeil 11.5.14 Synchronisierte Methodenaufrufe zusammenfassen
Pfeil 11.5.15 Deadlocks
Pfeil 11.6 Synchronisation über Warten und Benachrichtigen
Pfeil 11.6.1 Die Schnittstelle Condition
Pfeil 11.6.2 It’s Disco-Time
Pfeil 11.6.3 Warten mit wait() und Aufwecken mit notify()
Pfeil 11.6.4 Falls der Lock fehlt: IllegalMonitorStateException
Pfeil 11.6.5 Semaphor
Pfeil 11.7 Atomare Operationen und frische Werte mit volatile
Pfeil 11.7.1 Der Modifizierer volatile bei Objekt-/Klassenvariablen
Pfeil 11.7.2 Das Paket java.util.concurrent.atomic
Pfeil 11.8 Mit dem Thread verbundene Variablen
Pfeil 11.8.1 ThreadLocal
Pfeil 11.8.2 InheritableThreadLocal
Pfeil 11.9 Gruppen von Threads in einer Thread-Gruppe
Pfeil 11.9.1 Aktive Threads in der Umgebung
Pfeil 11.9.2 Etwas über die aktuelle Thread-Gruppe herausfinden
Pfeil 11.9.3 Threads in einer Thread-Gruppe anlegen
Pfeil 11.9.4 Methoden von Thread und ThreadGroup im Vergleich
Pfeil 11.10 Zeitgesteuerte Abläufe
Pfeil 11.10.1 Die Klassen Timer und TimerTask
Pfeil 11.10.2 Job-Scheduler Quartz
Pfeil 11.11 Einen Abbruch der virtuellen Maschine erkennen
Pfeil 11.12 Zum Weiterlesen


Galileo Computing - Zum Seitenanfang

11.4 Der Ausführer (Executor) kommt Zur nächsten ÜberschriftZur vorigen Überschrift

Damit der Thread ein Runnable ausführt, muss immer ein neues Thread-Objekt aufgebaut werden, denn start() ist nur einmal auf einem Thread-Objekt erlaubt. Würde der Thread während seines Lebenszyklus eine Reihe unterschiedlicher Runnable abarbeiten wollen, ist eine andere Umsetzung nötig. Das Gleiche gilt, wenn das Runnable nicht sofort, sondern später (Ostern) oder wiederholt (immer Weihnachten) ausgeführt werden soll.


Galileo Computing - Zum Seitenanfang

11.4.1 Die Schnittstelle Executor Zur nächsten ÜberschriftZur vorigen Überschrift

Seit Java 5 gibt es eine Abstraktion für Klassen, die Befehle über Runnable ausführen. Die Schnittstelle Executor schreibt eine Methode vor:


interface java.util.concurrent.Executor

  • void execute( Runnable command )
    Wird später von Klassen implementiert, die ein Runnable abarbeiten können.

Jeder, der nun Befehle über Runnable abarbeitet, ist Executor.

Konkrete Executoren

Von dieser Schnittstelle gibt es bisher zwei wichtige Implementierungen:

  • ThreadPoolExecutor. Die Klasse baut eine Sammlung von Threads auf, den Thread-Pool. Ausführungsanfragen werden von den freien Threads übernommen.
  • ScheduledThreadPoolExecutor. Eine Erweiterung von ThreadPoolExecutor um die Fähigkeit, zu bestimmen Zeiten oder mit bestimmten Wiederholungen Befehle abzuarbeiten.

Die beiden Klassen haben nicht ganz so triviale Konstruktoren, und eine Utility-Klasse vereinfacht den Aufbau dieser speziellen Executor-Objekte.


class java.util.concurrent.Executors

  • static ExecutorService newCachedThreadPool()
    Liefert einen Thread-Pool mit wachsender Größe.
  • static ExecutorService newFixedThreadPool( int nThreads )
    Liefert einen Thread-Pool mit maximal nThreads.
  • static ScheduledExecutorService newSingleThreadScheduledExecutor()
  • static ScheduledExecutorService newScheduledThreadPool( int corePoolSize )
    Gibt spezielle Executor-Objekte zurück, um Wiederholungen festzulegen.

ExecutorService ist eine Schnittstelle, die Executor erweitert. Unter anderem sind hier Operationen zu finden, die die Ausführer herunterfahren. Im Falle von Thread-Pools ist das nützlich, da die Threads ja sonst nicht beendet würden, weil sie auf neue Aufgaben warten.


Galileo Computing - Zum Seitenanfang

11.4.2 Die Thread-Pools Zur nächsten ÜberschriftZur vorigen Überschrift

Eine wichtige statische Funktion der Klasse Executors ist newCachedThreadPool(). Das Ergebnis ist ein ExecutorService-Objekt, eine Implementierung von Executor mit der Methode execute(Runnable).

Listing 11.10 com/tutego/insel/thread/concurrent/ThreadPoolDemo.java, main()

Runnable r1 = new Runnable() { 
  @Override public void run() { 
    System.out.println( "A1 " + Thread.currentThread() ); 
    System.out.println( "A2 " + Thread.currentThread() ); 
  } 
}; 
 
Runnable r2 = new Runnable() { 
  @Override public void run() { 
    System.out.println( "B1 " + Thread.currentThread() ); 
    System.out.println( "B2 " + Thread.currentThread() ); 
  } 
};

Jetzt lässt sich der Thread-Pool als ExecutorService beziehen und lassen sich die beiden Befehlsobjekte als Runnable über execute() ausführen:

ExecutorService executor = Executors.newCachedThreadPool(); 
 
executor.execute( r1 ); 
executor.execute( r2 ); 
 
Thread.sleep( 500 ); 
 
executor.execute( r1 ); 
executor.execute( r2 ); 
 
executor.shutdown();

Die Ausgabe zeigt sehr schön die Wiederverwendung der Threads:

A1 Thread[pool-1-thread-1,5,main] 
A2 Thread[pool-1-thread-1,5,main] 
B1 Thread[pool-1-thread-2,5,main] 
B2 Thread[pool-1-thread-2,5,main] 
B1 Thread[pool-1-thread-1,5,main] 
B2 Thread[pool-1-thread-1,5,main] 
A1 Thread[pool-1-thread-2,5,main] 
A2 Thread[pool-1-thread-2,5,main]

Die toString()-Methode von Thread ist so implementiert, dass zunächst der Name der Threads auftaucht, den die Pool-Implementierung gesetzt hat, dann die Priorität und der Name des Threads, der den neuen Thread gestartet hat. Am neuen Namen ist abzulesen, dass hier zwei Threads von einem Thread-Pool 1 verwendet werden: thread-1 und thread-2. Nach dem Ausführen der beiden Aufträge und der kleinen Warterei sind die Threads fertig und für neue Jobs bereit, sodass A1 und A2 beim zweiten Mal mit den wieder freien Threads abgearbeitet werden.

Interessant sind die folgenden drei Operationen zur Steuerung des Pool-Endes:


interface java.util.concurrent.ExecutorService 
extends Executor

  • void shutdown()
    Fährt den Thread-Pool herunter. Laufende Threads werden nicht abgebrochen, aber neue Anfragen werden nicht angenommen.
  • boolean isShutdown()
    Wurde der Executor schon heruntergefahren?
  • List<Runnable> shutdownNow()
    Gerade ausführende Befehle werden zum Stoppen angeregt. Die Rückgabe ist eine Liste der zu beendenden Kommandos.

Galileo Computing - Zum Seitenanfang

11.4.3 Threads mit Rückgabe über Callable Zur nächsten ÜberschriftZur vorigen Überschrift

Der nebenläufige Thread kann nur über Umwege dem aufrufenden Programm Ergebnisse mitteilen. In einer eigenen Klasse, die Runnable erweitert, lässt sich im Konstruktor zum Beispiel eine Datenstruktur übergeben, in die der Thread das Ergebnis hineinlegt. Die Datenstruktur kann dann vom Aufrufer auf Änderungen untersucht werden.

Seit Java 5 gibt es neben Runnable die Schnittstelle Callable, die dem Aufrufer eine Rückgabe übermittelt:


interface java.util.concurrent.Callable<V>

  • V call()
    Diese Methode enthält den parallel auszuführenden Programmcode und liefert eine Rückgabe vom Typ V.

Felder sortieren über Callable

Wir wollen nun ein Beispiel angeben, das ein Feld sortiert. Das Sortieren soll ein Callable im Hintergrund übernehmen. Ist die Operation beendet, soll der Verweis auf das Feld zurückgegeben werden. Das Sortieren erledigt wie üblich Arrays.sort().

Listing 11.11 com/tutego/insel/thread/concurrent/CallableDemo.java, SorterCallable

class SorterCallable implements Callable<byte[]> 
{ 
  private final byte[] b; 
 
  SorterCallable( byte[] b ) 
  { 
    this.b = b; 
  } 
 
  @Override public byte[] call() 
  { 
    Arrays.sort( b ); 
 
    return b; 
  } 
}

Natürlich bringt es wenig, das Callable-Objekt aufzubauen und selbst call() aufzurufen, denn ein Thread soll die Aufgabe im Hintergrund erledigen. Dazu ist jedoch nicht die Klasse Thread selbst zu verwenden, sondern ein ExecutorService, den wir etwa über Executors.newCachedThreadPool() bekommen.

Listing 11.12 com/tutego/insel/thread/concurrent/CallableDemo.java, main()

byte[] b = new byte[ 4000000 ]; 
new Random().nextBytes( b ); 
Callable<byte[]> c = new SorterCallable( b ); 
ExecutorService executor = Executors.newCachedThreadPool();

Der ExecutorService bietet eine submit()-Methode, die das Callable annimmt und einen Thread für die Abarbeitung aussucht. Weil das Ergebnis asynchron ankommt, liefert submit() ein Future-Objekt zurück, über das wir herausfinden können, ob das Ergebnis schon da ist oder ob wir noch warten müssen.

Future<byte[]> result = executor.submit( c );

Nach dieser Anweisung ist die beste Zeit, noch andere parallele Aufgaben anzustoßen, um später die Ergebnisse einzusammeln.

Unsere Variable result verrät mit isDone(), ob die Aufgabe schon erledigt ist. Rufen wir get() auf, blockiert die Methode so lange, bis das Ergebnis da ist.

byte[] bs = result.get(); 
System.out.printf( "%d, %d, %d%n", bs[0], bs[1], bs[bs.length-1] ); // –128,  
                                                                    // –128, 127

Da das Feld sortiert ist, und der Wertebereich eines Bytes –128 bis +127 ist, ist vermutlich das kleinste Element der Zufallszahlen –128 und das größte 127.

Nicht immer ist das Blockieren erwünscht. Für diesen Fall ermöglicht die überladene Methode von get() eine Parametrisierung mit einer Wartezeit und Zeiteinheit:

byte[] bs = result.get( 2, TimeUnit.SECONDS );

Ist das Ergebnis nicht innerhalb von 2 Sekunden verfügbar, löst die Methode eine Exception aus, die, auf dem Bildschirm ausgegeben, so aussehen wird:

java.util.concurrent.TimeoutException 
  at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:228) 
  at java.util.concurrent.FutureTask.get(FutureTask.java:91) 
  at com.tutego.insel.thread.concurrent.CallableDemo.main(CallableDemo.java:27)

Ein Runnable mit Zukunft oder als Callable

Aus Gründen der Symmetrie gibt es neben submit(Callable) noch zwei submit()-Methoden, die ebenfalls ein Runnable annehmen. Zusammen ergeben sich:


interface java.util.concurrent.ExecutorService 
extends Executor

  • <T> Future<T> submit( Callable<T> task )
    Der ExecutorService soll die Aufgabe abarbeiten und Zugriff auf das Ergebnis über die Rückgabe geben.
  • Future<?> submit( Runnable task )
    Der ExecutorService arbeitet das Runnable ab und ermöglicht es, über das Future-Objekt zu erfragen, ob die Ausgabe schon abgearbeitet wurde oder nicht. get() liefert am Ende null.
  • <T> Future<T> submit( Runnable task, T result )
    Wie submit(task), nur: Die get()-Anfrage über Future liefert result.

Zum Umbau eines Runnable in ein Callable gibt es noch einige Hilfsfunktionen in der Klasse Executors. Dazu zählt die statische Methode callable(Runnable task), die ein Callable<Object> liefert und callable(Runnable task, T result), das ein Callable<T> zurückgibt.


Galileo Computing - Zum Seitenanfang

11.4.4 Mehrere Callable abarbeiten Zur nächsten ÜberschriftZur vorigen Überschrift

Die Methode submit()vom ExecutorService nimmt genau ein Callable an und führt es aus. Doch die Klasse kann auch mehrere Callable zur gleichen Zeit ausführen:


interface java.util.concurrent.ExecutorService 
extends Executor

  • <T> List<Future<T>> invokeAll( Collection<Callable<T>> tasks )
    Führt alle Ausgaben aus. Liefert eine Liste von Future-Objekten, die die Ergebnisse repräsentieren.
  • <T> List<Future<T>> invokeAll( Collection<Callable<T>> tasks, long timeout, TimeUnit unit ) Führt alle Ausgaben aus und würde die Ergebnisse als Liste von Future-Objekten liefern, wenn die Zeit timeout in der gegebenen Zeiteinheit nicht überschritten wird.
  • <T> T invokeAny( Collection<Callable<T>> tasks )
    Führt alle Aufgaben aus, aber liefert das Ergebnis des Threads, der als Erster fertig ist. Ein get() wird also nie warten müssen.
  • <T> T invokeAny( Collection<Callable<T>> tasks, long timeout, TimeUnit unit )
    Führt alle Aufgaben aus, gilt aber nur eine beschränkte Zeit. Das erste Ergebnis eines Callable-Objekts, das in der Zeit fertig wird, gibt invokeAny() zurück.

Galileo Computing - Zum Seitenanfang

11.4.5 Mit ScheduledExecutorService wiederholende Ausgaben und Zeitsteuerungen topZur vorigen Überschrift

Die Klasse ScheduledThreadPoolExecutor ist eine weitere Klasse neben ThreadPoolExecutor, die die Schnittstelle Executor und ExecutorService implementiert. Die wichtige Schnittstelle, die diese Klasse außerdem implementiert, ist aber ScheduledExecutorService – sie schreibt scheduleXXX()-Operationen vor, um ein Runnable oder Callable zu bestimmten Zeiten und Wiederholungen auszuführen. (Zwar gibt es mit dem java.util.Timer etwas Ähnliches, doch der ScheduledThreadPoolExecutor nutzt Threads aus dem Pool.)

Das folgende Beispiel führt nach einer Startzeit von einer Sekunde alle zwei Sekunden eine Ausgabe aus.

Listing 11.13 com/tutego/insel/thread/concurrent/ScheduledExecutorServiceDemo.java. main()

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool( 1 ); 
scheduler.scheduleAtFixedRate( 
    new Runnable() { 
      @Override public void run() { 
        System.out.println( "Tata" ); 
      } 
    }, 
    1 /* Startverzögerung */, 
    2 /* Dauer */, 
    TimeUnit.SECONDS );

Nach einer Sekunde Startverzögerung bekommen wir jede zweite Sekunde ein »Tata«.



Ihr Kommentar

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






<< zurück
  Zum Katalog
Zum Katalog: Java ist auch eine Insel





Java ist auch eine Insel
Jetzt bestellen


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

 Tipp
Zum Katalog: Coding for Fun





 Coding for Fun


 Buchempfehlungen
Zum Katalog: Objektorientierte Programmierung





 Objektorientierte
 Programmierung


Zum Katalog: Einstieg in Eclipse 3.4






 Einstieg in
 Eclipse 3.4


Zum Katalog: Java 6 lernen mit Eclipse






 Java 6 lernen
 mit Eclipse


Zum Katalog: NetBeans Platform 6






 NetBeans
 Platform 6


Zum Katalog: Java und XML






 Java und XML


Zum Katalog: Visual C# 2008






 Visual C# 2008


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: C++ von A bis Z






 C++ von A bis Z


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2009
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