|
|||||||||||||||||||
HOME | COURSES | TALKS | ARTICLES | GENERICS | LAMBDAS | IOSTREAMS | ABOUT | CONTACT | | | | |||||||||||||||||||
|
Java Multithread Support - Anhalten und Beenden von Threads
|
||||||||||||||||||
Nachdem wir in unserem letzten Artikel (/
KRE5
/)
einen Klassiker der Java Multithread-Programmierung, das Nested-Monitor-Problem,
diskutiert haben, wenden wir uns in diesem Artikel einer anderen klassischen
Java Multithread-Fragestellung zu: Wie kann man Threads in Java anhalten
oder beenden? Zu diesem Thema gehört unter anderem: Warum sollte man
Thread.stop() oder Thread.suspend() nicht benutzen, obwohl „wir bis jetzt
noch keine Probleme damit auf Sun’s JVM für Windows hatten“ (Zitat
aus einem Projekt)? Was hat es mit der InterruptedException auf sich, die
unter anderem von den Methoden Object.wait() bzw. Condition.await() geworfen
werden kann und deren Diskussion wir bisher immer wieder ausgeklammert
haben?
Die Geschichte von Thread.stop() und Thread.suspend()Seit dem JDK 1.0 gibt es in der Klasse Thread die beiden public Methoden start() und stop(). Die Methode start() startet den Thread und bringt die Methode run() zur Ausführung. Die Methode stop() dient nicht etwa dazu den Thread zu stoppen, sondern beendet ihn. Zum Stoppen (im Sinne von Anhalten) und Weiterlaufen sind die Methoden suspend() und resume() vorgesehen. Alle vier Methoden sind public Instanzmethoden. Sie können von Außen auf einem Thread-Objekt aufgerufen werden, um den Ablaufzustand eines Threads zu ändern (siehe auch Zustandsübergangsdiagramm in Abbildung 1).
Mit dem JDK 1.1 wurden stop(), suspend() und resume()) deprecated. Wenn eine JDK Methode als deprecated gekennzeichnet wird, dann bedeutet dies: die Methode soll nicht mehr benutzt werden. Um existierenden Code nicht zu brechen, bleibt die Methode aber im JDK API, selbst wenn ihre Benutzung mit Problemen verbunden sein kann. Und die Benutzung von stop()und suspend() kann mit Problemen verbunden sein. Warum? Dazu erinnern wir uns noch einmal an die Überlegungen aus unseren ersten beiden Artikeln über Multithread-Programmierung (siehe /KRE1/ und /KRE2/). Wir haben damals darüber diskutiert, dass ein synchronized Block (bzw. eine synchronized Methode) einen kritischen Bereich bildet, der von anderen Threads wie eine atomare Operation gesehen wird. Er dient dazu, die Attribute eines Objekts konsistent zu ändern. Als Beispiel haben wir einen Stack implementiert, der int Werte speichern kann: public class IntStack {Sehen wir uns noch einmal die pop() Methode an. Sie ist synchronized, damit sowohl die Abfrage des Stackpointers cnt sowie seine Veränderung und Nutzung als auch der Zugriff auf das interne Arrays array (in array[--cnt]) atomar sind und nicht von einem anderen Thread unterbrochen werden können. Was wäre nun, wenn stop() auf dem Thread aufgerufen wird, der gerade die pop() Methode ausführt? Der Thread wird mitten in dem kritischen Bereich (denn die ganze Methode ist ja synchronized) unterbrochen. Da der Thread beendet wird, werden alle Mutexsperren, die von dem Thread gehalten werden, automatisch freigegeben. Das macht auch Sinn, denn andernfalls würden diese Objekte für alle weiterhin existierenden Threads gesperrt bleiben. Nehmen wir an, die Unterbrechung durch stop() erfolgt im Statement: return array[--cnt] und zwar nachdem der Stackpointer cnt dekrementiert wurde, aber bevor der Wert aus dem array herausgelesen und zurückgegeben wurde. Durch die Unterbrechung ist der oberste Eintrag im Stack für immer verschwunden. Abstrakt betrachtet besteht das Problem darin, dass ein Thread innerhalb eines kritischen Bereichs beendet werden kann. Dies kann wiederum dazu führen, dass das gerade manipulierte Objekt inkonsistent wird. Wenn dieses Objekt dann weiterhin von anderen Threads zugreifbar ist, wirkt sich die Inkonsistenz auch auf den Rest des Programms aus. Bevor wir uns die Alternativen zu Thread.stop() für das Beenden eines Threads ansehen, schauen wir, was mit Thread.suspend() nicht in Ordnung ist. Man könnte jetzt vermuten, dass die Situation bei suspend() eine ähnliche wie bei stop() ist. Aber dem ist nicht so. Im Gegensatz zu stop() hebt suspend() nämlich die Mutexsperren des Threads nicht auf. Aber das kann auch ein Problem sein. Schauen wir uns dazu ein Beispiel mit unserem IntStack an. Nehmen wir an, wir haben drei Threads, die auf ein IntStack Objekt mit Kapazität 1000 Zugriff haben. Ein Thread ist der Produzent. Er trägt mit push() die Werte in den Stack ein. Ein anderer Thread ist der Konsument. Er holt mit pop() die Werte aus dem Stack heraus. Der dritte Thread übernimmt die Flusskontrolle. Er hält den Produzenten mit suspend() an, wenn mehr als 800 Werte im Stack sind, und startet ihn wieder mit resume(), wenn es weniger als 500 sind. Um den Füllstand des Stacks zu ermitteln, ruft er die Methode size() des Stacks alle zwei Sekunden auf. Dieser Pollmechanism ist zwar nicht besonders elegant, aber für unser Beispiel reicht er uns. Im praktischen Test wird unser System immer wieder sporadisch vollständig hängen. Das wird immer dann sein, wenn der suspend()-Aufruf durch den Flusskontroll-Thread den Produzenten-Thread so trifft, dass dieser im push() angehalten wird. Während seiner Wartezeit hält der Produzenten-Thread weiter das Mutex, das mit seinem Eintritt in push() gesperrt wurde. Leider bleibt dadurch der Produzent beim nächsten Aufruf vom pop() hängen. Er bekommt die Sperre des Mutex nicht. Der Inhalt des Stacks kann deshalb nicht mehr verändert werden. Das hat zur Folge, dass der Flusskontroll-Thread stets denselben hohen Füllstand beim Aufruf von size() zurückbekommt und somit das resume() nie wieder auf dem Produzenten-Thread aufrufen wird. Diese Stillstand-Situation kann nur durch Neustart wieder behoben werden. Das Ganze tritt nur sporadisch auf, weil der Konsumenten-Thread nicht nur push() aufruft, sondern auch die Werte erzeugen muss. Trifft ihn das suspend() in einer solchen Situation, dann entstehen keine Probleme. Es gab noch einen weiteren kleinen Haken mit der Benutzung von suspend() und resume(). Ein resume(), das auf einem Thread aufgerufen worden ist, der nicht suspendiert ist, geht für immer verloren. Das heißt, in Situationen, in denen es zu sogenannten Race Conditions kommen kann (d.h. es ist von der Programmlogik nicht sichergestellt, ob erst suspend() oder resume() aufgerufen wird), können diese Methoden nicht benutzt werden.
All diese Überlegungen haben dann dazu geführt, dass suspend(),
resume() und stop() zum JDK1.1 deprecated geworden, weil man mittlerweile
schlauer geworden war.
Welche Alternative gibt es zu Thread.stop() und Thread.suspend()Schaut man heute (z.B. JDK 1.4.2 oder 1.5) auf die Instanzmethoden der Klasse Thread, so hat man das Gefühl, dass es keinen Ersatz für stop() und suspend() gibt. Dies hat häufig zur Folge, dass es ziemlich verlockend ist, diese beide Methoden weiterhin zu benutzen. Das führt dann zu Fehlern, wie wir sie oben diskutiert haben. Die Praxis zeigt zwar meistens, dass diese Fehler eher selten auftreten (siehe Zitat zu Anfang dieses Artikels: „wir hatten bis jetzt noch keine Probleme damit auf Sun’s JVM für Windows“). Trotzdem sollte man den Verlockungen wiederstehen.Zugegeben, die Unterstützung durch den JDK bei der Nutzung von Alternativen zu stop() und suspend() ist nicht besonders umfangreich. Aber das hat seine Ursachen in dem grundsätzlichen Ansatz der Alternativen. Das Problem von stop() und suspend() ist im wesentlichen, dass diese Methoden den Zustands des Threads verändern können, während er in einem synchronized Block (bzw. einer synchronized Methode) ist. Das bedeutet, dass er gerade eine Reihe von Statements ausführt, die nach Außen atomar sein müssen. Wie wir gesehen haben, ist es weder eine gute Idee, ihn dabei zu unterbrechen und die Sperre freizugeben (wie beim stop()), noch ihn zu unterbrechen und die Sperre zu erhalten (wie beim suspend()). Besser wäre es doch, wenn die Unterbrechung des Threads dann erfolgte, wenn er gerade nicht in einem synchronized Block (bzw. einer synchronized Methode) ist. Es könnte sogar so funktionieren, dass der Auftrag für die Unterbrechung immer angenommen werden kann, die eigentliche Unterbrechung des Threads aber erst dann erfolgt, wenn der Thread gerade keine Sperren hält. Es gibt aber keine neuen Methoden des Threads APIs, die das für einen tun. Stattdessen ist die Philosophie, dass der Thread (genauer gesagt der Thread-Code) es am besten selbst weiß, wenn er in keinem kritischen Bereich ist. Dann kann er sich selbst beenden oder warten, falls er von Außen eine Aufforderung dazu bekommen hat. Der Mechanismus, solche Aufforderungen an einen Thread zuzustellen, ist neu zum JDK 1.1 API dazugekommen, als stop() und suspend() weggefallen (bzw. deprecated erklärt worden) sind. Wir schauen uns den neuen Mechanismus für Thread-Unterbrechungen im folgenden an. Thread UnterbrechungenSeit dem JDK 1.1 hat jeder Thread einen Unterbrechungsanforderungszustand (kurz: Interrupt-Status). Die Methode Thread.interrupt() nimmt die Anforderung zur Unterbrechung an einen Thread entgegen und setzt den Unter- brechungsanforderungszustand entsprechend. Sie ist eine public Instanzmethode, die von Außen auf dem Thread-Objekt aufgerufen werden kann.Die Art und Weise, wie die Unterbrechungsanforderung an einen Thread zugestellt wird, ist abhängig von der Methode, die der Thread gerade ausführt. Im Normalfall ist es Aufgabe des jeweiligen Threads, regelmäßig die Methode Thread.interrupted() aufzurufen. interrupted() ist eine statische Methode, die den Anforderungszustand des jeweils aktuellen Threads zurückliefert. Sie liefert true zurück, wenn eine Unterbrechungsanforderung an den aktuellen Thread gestellt wurde. Dabei ist zu beachten, dass nur einmal true zurückgeliefert wird. Die Methode interrupted() setzt den Anforderungszustand des Threads wieder zurück und bei weiteren Aufrufen von interrupted() wird false zurückgeliefert, bis wieder eine neue Unterbrechungsanforderung mit interrupt() von Außen gestellt wird. Nun könnte man denken, dass pro Unterbrechungsanforderung (d.h. pro Aufruf von interrupt()) genau einmal true von der Methode interrupted() zurückgeliefert wird. Das ist aber nicht so. Wenn bei bereits bestehender Unterbrechungsanforderung wiederholt interrupt() von Außen aufgerufen wird, so führen alle diese Aufrufe nur zur Zurückgabe eines einzigen true, und zwar beim nächsten Aufruf von interrupted(). Das Zustandsdiagramm in Abbildung 2 zeigt dieses Verhalten. Damit interrupt() nicht überflüssigerweise aufgerufen wird, kann von Außen mit der Instanzmethode Thread.isInterrupted() der aktuelle Unterbrechungsanforderungszustand des Threads von jedem geprüft werden, der Zugriff auf das Thread-Objekt hat. Abbildung 2: Thread-Zustände im Zusammenhang mit Unterbrechungsanforderungen Threads, die länger wartende Aufrufe wie Object.wait(), Thread.sleep(), Thread.join() oder Methoden aus dem JDK 1.5 wie Lock.lockInterruptibly() oder Condition.await() aufrufen, haben nicht die Möglichkeit regelmäßig interrupted() aufzurufen. Deshalb werfen diese Methoden eine InterruptedException, um anzuzeigen, dass mit interrupt() eine Unterbrechungsanforderung gestellt wurde. Das Werfen der Exception setzt auch den Anforderungszustand zurück, so dass ein nachfolgender Aufruf von interrupted() wieder false liefert, wenn keine erneute Anforderung gestellt wurde. Im JDK 1.5 gibt es übrigens zusätzlich zu dem Lock.lockInterruptibly() ein Lock.lock(), das nicht von einer Unterbrechungsanforderung an den Thread unterbrochen wird und also auch keine InterruptedException wirft. Dies gilt in gleicher Form auch für das mit einem Objekt assoziierte Mutex, das mit dem Keyword synchronized gesperrt wird. Auch hier wird das Warten auf die Sperre nicht abgebrochen, wenn der Thread eine Unterbrechungsanforderung erhält. Eine Alternative wie beim expliziten Lock gibt es beim Warten an einem synchronized-Block aber nicht. Die Ergänzung im JDK 1.5 ist hier deutlich flexibler; was übrigens auch für die Condition Abstraktion aus dem JDK 1.5 gilt. Sie bietet sowohl unterbrechbare (await()) wie ununterbrechbare Methoden (awaitUninterruptibly()) an, um auf das Eintreffen der Bedingung zu warten.
Weitere Besonderheiten bei der Reaktion auf Unterbrechungsanforderungen
gibt es bei der NIO (New I/O im Package java.nio), die seit der Version
1.4 Bestandteil des JDK ist. Die erste Besonderheit findet man bei den
wartenden (englisch: blocking) Ein-/Ausgabeoperation: wenn man eine wartende
Ein-/Ausgabeoperation der NIO auf einem InterruptibleChannel aufgerufen
hat, so wird diese durch eine Unterbrechungsanforderung mit der ClosedByInterruptException
abgebrochen und der Channel geschlossen. Man bekommt also eine andere
Exception als sonst, d.h. nicht die übliche InterruptedException.
Der Anforderungszustand wird nicht zurückgesetzt, d.h. der nächste
Aufruf von interrupted() liefert true. Auch das ist anders als beim Abbruch
der anderen wartenden oder länger andauernden Methoden wie Object.wait(),
Thread.sleep(), Thread.join()oder Condition.await(), usw.
Für alle wartenden Ein-/Ausgabeoperationen der klassischen I/O aus dem Package java.io gilt, dass sie durch Unterbrechungsanforderungen nicht beeinflusst werden, d.h es kann ganz normal nach Rückkehr der Methode mit interrupted() der Anforderungszustand geprüft werden. Zusammenfassend lässt sich sagen, dass das Ausliefern der Unterbrechungsanforderung an den zu unterbrechenden Thread höchst unterschiedlich erfolgen kann, je nachdem ob und welche wartende Methode gerade durch den Thread aufgerufen wurde. Dabei hat jede JDK Erweiterung es jedes Mal besser machen wollen, was zu leichten Inkonsistenten geführt hat: Mal hat die Threadunterbrechung gar keinen Einfluss. Dann wird eine Exception geworfen und der Anforderungszustand zurückgesetzt. Mal wird eine Exception geworfen und der Anforderungszustand nicht zurückgesetzt. Und dann gibt es noch die Möglichkeit, dass die Methode sofort ohne Exception zurückkehrt und der Anforderungszustand selbst überprüft werden muss. Je nach Konfiguration, können beim Aufruf von interrupt() auch noch Security-Aspekte eine Rolle spielen. Wenn kein SecurityManager vorhanden ist, funktioniert der Aufruf von interrupt() ohne jede Einschränkungen. Wenn ein SecurityManager vorhanden ist, dann wird in der interrupt() Methode die checkAccess(Thread t) Methode des SecurityManagers aufgerufen, wobei als Argument der Thread mitgegeben wird, der unterbrochen werden soll. Im Defaultfall, also bei einem Standard-SecurityManager, prüft die checkAccess Methode, ob der zu unterbrechende Thread ein Systemthread ist, d.h. ob er in der Threadgruppe mit Parent null ist. Wenn der zu unterbrechende Thread ein Systemthread ist, dann wird geprüft, ob der aktuelle Thread die RuntimePermission mit Wert modifyThread bezüglich des zu unterbrechenden Threads hat; wenn der zu unterbrechende Thread ein Userthread ist, wird nichts geprüft. Dieses Verhalten der checkAccess Methode kann in einem abgeleiteten, benutzerspezifischen SecurityManager redefiniert werden. Dabei empfiehlt die JavaDoc als einzige mögliche Implementierung, die oben beschriebene Security-Policy von den Systemthreads auf alle Threads auszudehnen. Das heißt, bei einem benutzerspezifischen SecurityManager wird auch für Userthreads geprüft, ob die richtige RuntimePermission vorliegt. Immer dann wenn die Methode SecurityManager.checkAccess(Thread t) den Aufruf aus Sicherheitsgründen nicht zulässt, wird interrupt() mit einer SecurityException abgebrochen. Unterbrechung und was dann ?Nachdem wir uns ausführlich mit dem Unterbrechungsmechanismus von Threads beschäftigt haben, wollen wir nun schauen, wie man diesen Mechanismus nutzen kann, um einen Thread anzuhalten bzw. zu beenden.Thread beendenFangen wir mit der einfachsten Situation an. Nehmen wir an, wir wollen, dass sich ein Thread beendet, wenn interrupt() auf seiner Threadinstanz aufgerufen wurde. Wenn wir annehmen, dass der Thread in seiner run() Methode eine forever-Schleife hat, die er zyklisch durchläuft, so sollte er in dieser Schleife regelmäßig den Unterbrechungsstatus überprüfen und sich wenn nötig beenden:public void run() {Dabei steht die Abfrage if(Thread.interrupted()) außerhalb eines synchronized Blocks; die notwendigen Synchronisationen verbergen sich in der „... Funktionalität des Threads ...“. Damit soll sichergestellt sein, dass alle Objekte, die währende des Ablaufs des Threads manipuliert werden, in einem konsistenten Zustand sind, wenn der Unterbrechungszustand abgefragt wird. Der Thread kann also im Falle einer bestehenden Unterbrechungsanforderung gefahrlos mit return beendet werden. Was machen wir mit den Fällen, in denen die Unterbrechungsanforderung durch eine InterruptedExceptions signalisiert wurde? Da der Unterbrechungszustand durch das Zustellen der Exception zurückgesetzt wurde, macht ein Aufruf von interrupted() keinen Sinn; er liefert ohnehin immer false. Aus diesem Grund ist es sinnvoll, im catch-Block einer InterruptedException wieder interrupt() zu rufen, damit unsere bereits in der forever-Schleife vorhandene Abfrage weiterhin funktioniert. Das könnte so aussehen: ...Natürlich könnte man auch ein eigenes internes Flag nutzen, das im catch-Block gesetzt wird und zusätzlich zu interrupted() in der forever-Schleife abgefragt wird. Im allgemeinen ist dies aber nicht nötig. Da die Unterbrechungsanforderung als Aufforderung zur Beendigung des Threads angesehen wird, empfiehlt es sich, vor jeder langandauernden Aktion im Thread erst einmal zu prüfen, ob eine Unterbrechungsanforderung vorliegt. Wenn ja, dann wird man die längeren Aktionen gar nicht erst anstoßen. Beispielsweise wird man bei vorliegenden Unterbrechungsanforderung keine wartenden (d.h. blocking) Methoden mehr aufrufen. Aufwändige Verarbeitungen innerhalb des Threads würde man ebenfalls im Falle einer bestehenden Unterbrechungsanforderung überspringen. Das gleiche gilt insbesondere auch für Methoden, die nicht auf Grund der Unterbrechungsanforderung sofort abgebrochen werden. Ein Beispiel für eine solche Methode ist die read() Methode auf dem InputStream eines Sockets in der klassichen I/O; die read() Methode kehrt erst dann zurück, wenn sie mit dem Lesen fertig ist, ganz egal ob zwischendurch eine Unterbrechungsaufforderung eingegangen ist oder nicht. Mit der folgenden Abfrage kann man den Aufruf von read() ggf. unterdrücken: ...Die Abfrage des Unterbrechungszustands erfolgt am besten mit isInterrupted(), weil isInterrupted() den Unterbrechungszustand wirklich nur abfragt, nicht aber ändert, während interrupted() den Unterbrechungszustand sofort zurücksetzt. Thread anhaltenEs ist etwas aufwändiger, die Funktionalität zu implementieren, die es ermöglicht, einen Thread anzuhalten und weiterlaufen zu lassen. Dazu werden wir den Synchronisationsmechanismus von wait() und notify() nutzen. Das heißt, das Warten des Threads wird durch einen Aufruf von wait() realisiert und die Aufforderung zum Weiterlaufen durch den Aufruf von notify(). Die Implementierung eines solchen Threads könnte zum Beispiel so aussehen:public class MyThread extends Thread {Die Methode resumeThread() ist nur deshalb synchronized, damit notify() aufgerufen werden kann. Ähnliches gilt für den synchronized Block um wait(). Beides sind also keine richtigen kritischen Bereiche, in denen konsistente Datenmanipulationen stattfinden soll. Befremdlich mag auf den ersten Blick die while()-Schleife um das wait() in der run() Methode erscheinen. Sie dient zusammen mit dem Flag isWaiting dazu, den Thread in ein erneutes wait() zu bringen, falls eine InterruptedException das wait() abgebrochen hat. Das könnte passieren, wenn während des Wartens interrupt() erneut aufgerufen wurde (z.B. weil suspendThread() erneut aufgerufen wurde). Bei einer erneuten Unterbrechungsaufforderung soll der Thread aber weiter warten und nicht plötzlich aufwachen. isWaiting wird deshalb erst dann auf false gesetzt, wenn das wait() regulär durch ein notify() aus der Methode resumeThread() verlassen wird. Das isWaiting Flag können wir zusätzlich in der Methode isSuspended() nutzen, damit von Außen abgefragt werden kann, ob der Thread im Wartezustand ist. Unabhängig davon, ob wir interrupt() als Aufforderung zum Beenden oder als Aufforderung zum Anhalten des Thread interpretieren, gilt nach wie vor die schon diskutierten Regeln: Im Thread manipulierte Objekte müssen vor dem Aufruf von wait() in einem konsistenten Zustand sein. Der Unterbrechungsstatus muss in catch-Blöcken, in denen InterruptedExceptions gefangen wurden, wieder gesetzt werden. Teile des Code können bei Bedarf mit der Abfrage if (!Thread.currentThread().isInterrupted()) übersprungen werden. Weitere FälleIm obigen Beispiel haben wir diskutiert, wie man einen Thread implementieren kann, der interrupt() als Aufforderung zum Anhalten interpretiert. Man kann die Threadunterbrechung auch benutzen, um den Thread sowohl anzuhalten als auch zu beenden (indem man etwa eine stopThread() hinzufügt). Um ein solches Verhalten zu implementieren, bedarf es einer weiteren Instanzvariable, um die Details der Anforderung (Beenden oder Anhalten) zu kommunizieren. Die Implementierungsdetails ergeben sich relativ einfach aus den oben bereits diskutierten Beispielen.Wenn man in diesem Fall sowieso eine Variable für die Anforderungsdetails braucht, könnte man auf die Idee kommen, dass man den Standardunterbrechungsmechanismus gar nicht nutzt, sondern nur die Variable abfragt. Das wäre grundsätzlich möglich. Man sollte aber bedenken, dass die Standardlösung einige wartende Aufrufe (wie wait(), sleep(), usw.) unterbricht, was bei der eigenen Lösung über die Variable nicht geht. Zusätzlich ist eine Implementierung auf Basis der Standardlösung vielleicht etwas wartbarer, weil man voraussetzen kann, dass der Standardmechanismus in der Java Community bekannt ist und zum Grundwissen der Java-Programmierer gehört. Es gibt übrigens im Threadunterbrechungsmechanismus nichts, das vorschreibt, dass der Mechanismus nur als Ersatz für stop() und suspend() genutzt werden kann. Grundsätzlich ist man bei der Threadimplementierung frei, auch andere Anforderungen an einen Thread über diesen Mechanismus zu kommunizieren. Ein Beispiel wäre die Interpretation von interrupt() als Aufforderung zur teilweisen oder vollständigen Reinitialisierung eines Threads; man könnte den Thread beispielsweise zurücksetzen oder Teile seines Verhaltens (z.B. das Logging-Level) ändern. ZusammenfassungIn diesem Artikel haben wir uns die Java-Mechanismen für das Anhalten und Beenden von Threads angesehen. Die ursprünglich dafür vorgesehenen Methoden Thread.stop() und Thread.suspend() sind seit JDK 1.1 deprecated, weil sie den betreffenden Thread sofort zwangsweise abbrechen und dadurch Probleme hervorrufen. Als Ersatz gibt es die Threadunterbrechungsaufforderung über Thread.interrupt(). Die Unterbrechungsanforderung ist eine Art Protokoll zwischen dem Thread, der die Unterbrechung anfordert, und dem Thread, der die Anforderung bekommt. Der empfangende Thread wird durch die Unterbrechungsaufforderung nicht gewaltsam abgebrochen, sondern er bekommt eine freundliche Aufforderung, sich doch bitte zu beenden. Er kann und muß dann selbst entscheiden, wie und wann er auf die Anforderung reagieren will. Wie man sinnvolle Reaktionen implementieren kann, haben wir an verschiedenen Beispielen diskutiert.Literaturverweise
|
|||||||||||||||||||
© Copyright 1995-2008 by Angelika Langer. All Rights Reserved. URL: < http://www.AngelikaLanger.com/Articles/EffectiveJava/17.StopThread/17.StopThread.html> last update: 26 Nov 2008 |