![]() |
|
|
Zusammenfassung: interrupted(), isInterrupted() und interrupt()Die Methodennamen sind verwirrend gewählt, so dass wir die Aufgaben noch einmal zusammenfassen wollen. Die Objektmethode interrupt() setzt in einem (anderen) Thread-Objekt ein Flag, dass es einen Antrag gab, den Thread zu beenden – es beendet nicht den Thread, obwohl der Methodenname das vermuten lässt. Dieses Flag lässt sich mit der Objektmethode isInterrupted() abfragen. In der Regel wird dies innerhalb einer Schleife geschehen, die darüber bestimmt, ob die Aktivität des Threads fortgesetzt werden soll. Die Klassenmethode interrupted() ist dagegen mehr als nur eine Anfragemethode (daher fehlt auch der Präfix is-). Sie testet zwar das entsprechende Flag des aktuell laufenden Threads, wie currentThread().isInterrupted(), aber modifiziert ihn auch, so dass er danach gelöscht ist. Zwei aufeinander folgende Aufrufe von interrupted() führen daher zu einem false, es sei denn, in der Zwischenzeit kam eine weitere Unterbrechung. 9.3.3 Der stop() von außen
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
class java.lang. Thread implements Runnable |
| void stop() Wurde der Thread gar nicht gestartet, kehrt die Funktion sofort zurück. Andernfalls wird über checkAccess() geprüft, ob wir überhaupt das Recht haben, den Thread abzuwürgen. Dann wird der Thread beendet, egal was er gerade gemacht hat, und das Letzte, was der Thread jetzt noch kann, ist sein Testament in Form eines ThreadDeath-Objekts als Exception anzuzeigen. |
| void checkAccess() Findet heraus, ob wir die Möglichkeit haben, den Zustand oder die Eigenschaften des Thread-Objekts von außen zu ändern. checkAccess() der Klasse Thread ruft die check Access()-Methode vom Security-Manager auf; möglicherweise bekommen wir eine SecurityException. |
So unmöglich ist das Reagieren auf ein stop() auch nicht. Immer dann, wenn ein Thread mit stop() zu Ende kommen soll, löst die JVM eine ThreadDeath-Ausnahme aus, die letztendlich den Thread beendet. ThreadDeath ist eine Unterklasse von Error. Error ist wiederum von Throwable abgeleitet, so dass ThreadDeath mit einem try/catch-Block abgefangen werden kann. Die Java-Entwickler haben ThreadDeath nicht zu einer Unterklasse von Exception gemacht, weil sie nicht wollten, dass ThreadDeath bei einer allgemeinen Exception-Behandlung über catch(Exception e) abgefangen wird.
Wenn wir ThreadDeath auffangen, dann können wir noch auf den Tod reagieren und Aufräumarbeiten erlauben. Wir sollten aber nicht vergessen, anschließend das aufgefangene ThreadDeath-Objekt wieder auszulösen, denn sonst wird der Thread nicht beendet.
Listing 9.5 ThreadWiederbelebung.java
class ThreadWiederbelebung { public static void main( String args[] ) { class RunEndlos implements Runnable { public void run() { try { while ( true ) ; } catch ( ThreadDeath td ) { System.out.println( "Da haben wir aber noch mal Glück gehabt" ); throw td; } } } Thread t = new Thread( new RunEndlos() ); t.start(); t.stop(); } }
ThreadDeath kann auch verwendet werden, um das aktuell laufende Programm zu beenden (aber System.exit() ist weniger extravagant).
throw new ThreadDeath();
Ein Thread kann keine Ergebnisse wie eine Funktion nach außen geben, da die run()-Methode den Ergebnistyp void hat. Da ein nebenläufiger Thread zudem asynchron arbeitet, wissen wir noch nicht einmal, wann wir das Ergebnis erwarten können. Die Übertragung von Werten ist jedoch kein Problem. Hier können Klassenvariablen und auch Objektvariablen helfen, denn über sie können wir kommunizieren. Jetzt fehlt nur noch, dass wir auf das Ende der Aktivität eines Threads warten können. Das geht mit der Methode join().
| Beispiel Ein Thread T legt in der Variablen result ein Ergebnis ab. Wir können die Auswirkungen von join() sehen, wenn wir die auskommentierte Zeile hineinnehmen. |
Listing 9.6 JoinTheThread.java
class JoinTheThread { static class JoinerThread extends Thread { public int result; public void run() { result = 1; } } public static void main( String args[] ) throws Exception { JoinerThread t = new JoinerThread(); t.start(); // t.join(); System.out.println( t.result ); } }
Ohne den Aufruf von join() wird als Ergebnis 0 ausgegeben, denn das Starten des Threads kostet etwas Zeit. In dieser Zeit aber geben wir die nicht initialisierte Klassenvariable aus. Nehmen wir join() hinein, wird die run()-Methode zu Ende ausgeführt und der Thread setzt die Variable result auf 1. Das sehen wir dann auf dem Bildschirm.
class java.lang. Thread implements Runnable |
| final void join() throws InterruptedException Der aktuell ausgeführte Thread wartet auf den Thread, für den die Methode aufgerufen wird, bis dieser beendet ist. |
| final synchronized void join( long millis ) throws InterruptedException Wie join(), doch wartet diese Variante höchstens millis Millisekunden. Wurde der Thread bis dahin nicht vollständig beendet, fährt das Programm fort. Auf diese Weise kann versucht werden, innerhalb einer bestimmten Zeitspanne auf den Thread zu warten, sonst aber weiterzumachen. Ist millis gleich 0, so hat dies die gleiche Wirkung wie join(). |
| final synchronized void join ( long millis, int nanos ) throws InterruptedException Wie join(long), jedoch mit potenziell genauerer Angabe der maximalen Wartezeit |
Große Probleme können in mehrere Teile zerlegt werden, und jedes Teilproblem kann dann von einem Thread gelöst werden. Das ist insbesondere bei Mehrprozessorsystemen eine lohnenswerte Investition. Zum Schluss müssen wir nur noch darauf warten, dass die Threads zum Ende gekommen sind und das Ergebnis einsammeln. Dazu eignet sich join() gut.
Beispiel Zwei Threads A und B arbeiten an einem Problem. Eine Methode go() erzeugt die Threads und wartet, bis beide ihre Aufgabe erledigt haben. Dann könnte etwa ein anderer Thread die von A und B benutzten Ressourcen wieder nutzen.
void go() throws Exception { Thread a = new A(); Thread b = new B(); a.start(); b.start(); a. join() ; b. join() ; } |
Es ist unerheblich, wessen join() wir zuerst aufrufen, da wir sowieso auf den langsamsten Thread warten müssen. Wenn ein Thread schon beendet ist, dann kehrt join() sofort zurück.
Eine andere Lösung für zusammenlaufende Threads ist, diese in einer Thread-Gruppe zusammenzufassen. Dann können sie zusammen behandelt werden, so dass nur das Ende der Thread-Gruppe beobachtet wird.
Manchmal ist es notwendig, einen Thread für eine bestimmte Zeit anzuhalten. Dazu dient die überladende Klassenfunktion sleep(). Etwas erstaunlich ist sicherlich, dass das keine Objektfunktion ist, die jedes Thread-Objekt anbietet, sondern eine statische Funktion. Ein Grund wird sein, dass dadurch verhindert wird, externe Threads zu beeinflussen. Es ist nicht möglich, einen fremden Thread, dessen Referenz wir haben, einfach ein paar Sekunden schlafen zu legen.
class java.lang. Thread implements Runnable |
| static void sleep( long millis ) throws InterruptedException Der aktuell ausgeführte Thread wird mindestens millis Millisekunden eingeschläfert. Unterbricht ein anderer Thread den schlafenden, so wird vorzeitig eine Interrupted Exception ausgelöst. |
| static void sleep( long millis, int nanos ) throws InterruptedException Der aktuell ausgeführte Thread wird mindestens millis Millisekunden und zusätzlich nanos Nanosekunden eingeschläfert. Im Gegensatz zu sleep(long) wird bei einer negativen Millisekundenanzahl eine IllegalArgumentException ausgelöst, ebenso wird diese Exception ausgelöst, wenn die Nanosekundenanzahl nicht zwischen 0 und 999999 liegt. |
Beispiel Die Applikation soll zwei Sekunden lang schlafen.
try { Thread.sleep ( 2000 ); } catch ( InterruptedException e ) { } |
Die Unterbrechung sitzt in einem zwingenden try/catch-Block, da eine Exception ausgelöst wird, wenn der Thread unterbrochen wird.
Praktisch wird das Erweitern der Klasse Thread bei inneren anonymen Klassen. Die folgende Klasse SleepInInnerClass gibt nach zwei Sekunden Schlafzeit eine Meldung auf dem Bildschirm aus. Wir starten den Thread dabei aus dem Objekt-Initialisierungsblock. Natürlich hätten wir auch direkt auf der anonymen Unterklasse die Methode start() aufrufen können.
Listing 9.7 SleepInInnerClass.java
public class SleepInInnerClass { public static void main( String args[] ) { new Thread() { { start(); } { public void run() { try { sleep(2000); System.out.println("Zeit ist um."); } catch ( InterruptedException e ) { } } }; } }
Die sleep()-Methode kann auch effektiv zum Warten benutzt werden. Soll ein Programm zu einer bestimmten Zeit eine Aufgabe ausführen, beispielsweise um 18 Uhr eine Nachricht senden, wenn die Simpsons im Fernsehen laufen, so kann ein Thread eingesetzt werden.
Listing 9.8 Clock.java
import java.util.*; class Clock extends Thread { Calendar cal = new GregorianCalendar(); public Clock( int timeHour, int timeMinute, String title ) { wakeHour = timeHour; wakeMinute = timeMinute; this.title = title; start(); } public Clock( int timeHour, String title ) { this ( timeHour, 0, title ); } public void run() { boolean checked = false; while ( true ) { try { cal.setTime( new Date() ); if ( cal.get( Calendar.HOUR_OF_DAY ) == wakeHour && cal.get( Calendar.MINUTE ) == wakeMinute ) { if ( !checked ) { // hier nun Arbeit verrichten System.out.println( title ); checked = true; } } Thread.sleep( 1000*10 ); // 10 Sekunden schlafen } catch ( InterruptedException e ) {}; } } private int wakeHour, wakeMinute; private String title; public static void main( String args[] ) { Clock clock = new Clock( 18, "Simpsons kommt" ); } }
| << zurück |
Copyright © Galileo Press GmbH 2004
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.