Wie man den Faden in Java richtig stoppt?

Ich brauche eine Lösung, um den Thread in Java ordnungsgemäß zu stoppen.

Ich habe IndexProcessor class, die die Runnable-Schnittstelle implementiert:

 public class IndexProcessor implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class); @Override public void run() { boolean run = true; while (run) { try { LOGGER.debug("Sleeping..."); Thread.sleep((long) 15000); LOGGER.debug("Processing"); } catch (InterruptedException e) { LOGGER.error("Exception", e); run = false; } } } } 

Und ich habe ServletContextListener class, die den Thread startet und stoppt:

 public class SearchEngineContextListener implements ServletContextListener { private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class); private Thread thread = null; @Override public void contextInitialized(ServletContextEvent event) { thread = new Thread(new IndexProcessor()); LOGGER.debug("Starting thread: " + thread); thread.start(); LOGGER.debug("Background process successfully started."); } @Override public void contextDestroyed(ServletContextEvent event) { LOGGER.debug("Stopping thread: " + thread); if (thread != null) { thread.interrupt(); LOGGER.debug("Thread successfully stopped."); } } } 

Aber wenn ich Tomcat herunterfahre, bekomme ich die Ausnahme in meiner IndexProcessor-class:

 2012-06-09 17:04:50,671 [Thread-3] ERROR IndexProcessor Exception java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at lt.ccl.searchengine.processor.IndexProcessor.run(IndexProcessor.java:22) at java.lang.Thread.run(Unknown Source) 

Ich benutze JDK 1.6. Die Frage ist also:

Wie kann ich den Thread stoppen und keine Ausnahmen casting?

PS Ich möchte nicht verwenden .stop(); Methode, weil es veraltet ist.

In der IndexProcessor class benötigen Sie eine Möglichkeit, ein Flag zu setzen, das den zu terminierenden Thread ähnlich wie die Variable run angibt run die Sie nur im classnbereich verwendet haben.

Wenn Sie den Thread anhalten möchten, setzen Sie dieses Flag und rufen Sie join() für den Thread auf und warten Sie, bis er beendet ist.

Stellen Sie sicher, dass das Flag threadsicher ist, indem Sie eine flüchtige Variable verwenden oder indem Sie Getter- und Setter-Methoden verwenden, die mit der als Flag verwendeten Variablen synchronisiert werden.

 public class IndexProcessor implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class); private volatile boolean running = true; public void terminate() { running = false; } @Override public void run() { while (running) { try { LOGGER.debug("Sleeping..."); Thread.sleep((long) 15000); LOGGER.debug("Processing"); } catch (InterruptedException e) { LOGGER.error("Exception", e); running = false; } } } } 

Dann in SearchEngineContextListener :

 public class SearchEngineContextListener implements ServletContextListener { private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class); private Thread thread = null; private IndexProcessor runnable = null; @Override public void contextInitialized(ServletContextEvent event) { runnable = new IndexProcessor(); thread = new Thread(runnable); LOGGER.debug("Starting thread: " + thread); thread.start(); LOGGER.debug("Background process successfully started."); } @Override public void contextDestroyed(ServletContextEvent event) { LOGGER.debug("Stopping thread: " + thread); if (thread != null) { runnable.terminate(); thread.join(); LOGGER.debug("Thread successfully stopped."); } } } 

Die Verwendung von Thread.interrupt() ist ein vollkommen akzeptabler Weg, dies zu tun. In der Tat ist es wahrscheinlich besser als eine Flagge, wie oben vorgeschlagen. Der Grund dafür ist, dass Sie, wenn Sie sich in einem unterbrechbaren blockierenden Anruf befinden (wie Thread.sleep oder java.nio Channel-Vorgänge), tatsächlich in der Lage sind, sofort aus diesen herauszubrechen.

Wenn Sie ein Flag verwenden, müssen Sie warten, bis der Blockiervorgang abgeschlossen ist, und dann können Sie Ihr Flag überprüfen. In einigen Fällen müssen Sie dies trotzdem tun, z. B. mit dem Standard- InputStream / OutputStream die nicht unterbrechbar sind.

In diesem Fall, wenn ein Thread unterbrochen wird, unterbricht es den IO nicht, aber Sie können dies leicht routinemäßig in Ihrem Code tun (und Sie sollten dies an strategischen Punkten tun, wo Sie sicher anhalten und aufräumen können).

 if (Thread.currentThread().isInterrupted()) { // cleanup and stop execution // for example a break in a loop } 

Wie gesagt, der Hauptvorteil von Thread.interrupt() besteht darin, dass Sie unterbrechbare Aufrufe sofort unterbrechen können, was mit der Flag- Thread.interrupt() nicht möglich ist.

Einfache Antwort: Sie können einen Thread INTERN auf eine von zwei Arten stoppen:

  • Die Methode run trifft eine Rückgabeunterroutine.
  • Die Methode run beendet und gibt implizit zurück.

Sie können auch Threads EXTERN stoppen:

  • Rufen Sie system.exit (dies tötet Ihren gesamten process)
  • Rufen Sie die interrupt() Methode des Thread-Objekts auf *
  • Sehen Sie, ob der Thread eine implementierte Methode hat, die klingt, als würde sie funktionieren (wie kill() oder stop() )

*: Die Erwartung ist, dass dies einen Thread stoppen soll. Was der Thread tatsächlich tut, wenn dies geschieht, hängt jedoch vollständig von dem ab, was der Entwickler beim Erstellen der Thread-Implementierung geschrieben hat.

Ein übliches Muster, das Sie bei Methodenimplementierungen sehen, ist while(boolean){} , wobei boolean normalerweise etwas namens isRunning , eine Membervariable seiner isRunning , flüchtig und normalerweise für andere Threads von einer Setter-Methode zugänglich ist sortiert, zB kill() { isRunnable=false; } kill() { isRunnable=false; } . Diese Subroutinen sind nett, weil sie es dem Thread erlauben, alle Ressourcen freizugeben, die er vor dem Beenden besitzt.

Sie sollten Threads immer beenden, indem Sie ein Flag in der run() Schleife (falls vorhanden) markieren.

Dein Thread sollte folgendermaßen aussehen:

 public class IndexProcessor implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class); private volatile boolean execute; @Override public void run() { this.execute = true; while (this.execute) { try { LOGGER.debug("Sleeping..."); Thread.sleep((long) 15000); LOGGER.debug("Processing"); } catch (InterruptedException e) { LOGGER.error("Exception", e); this.execute = false; } } } public void stopExecuting() { this.execute = false; } } 

Dann können Sie den Thread thread.stopExecuting() indem Sie thread.stopExecuting() aufrufen. Auf diese Weise wird der Thread sauber beendet, aber das dauert bis zu 15 Sekunden (aufgrund Ihres Schlafes). Sie können thread.interrupt () immer noch aufrufen, wenn es wirklich dringend ist – aber der bevorzugte Weg sollte immer das Flag überprüfen.

Um zu vermeiden, dass Sie 15 Sekunden warten, können Sie den Schlaf folgendermaßen aufteilen:

  ... try { LOGGER.debug("Sleeping..."); for (int i = 0; (i < 150) && this.execute; i++) { Thread.sleep((long) 100); } LOGGER.debug("Processing"); } catch (InterruptedException e) { ... 

Zum Synchronisieren von Threads verwende ich lieber CountDownLatch was dazu beiträgt, dass Threads warten, bis der process abgeschlossen ist. In diesem Fall wird die Worker-class mit einer CountDownLatch Instanz mit einer bestimmten Anzahl eingerichtet. Ein Call-to- await Verfahren wird so lange blockiert, bis die aktuelle Anzahl aufgrund von Aufrufen der countDown Methode den countDown Null erreicht oder das Zeitlimit erreicht ist. Dieser Ansatz ermöglicht es, einen Thread sofort zu unterbrechen, ohne auf die angegebene Wartezeit warten zu müssen:

 public class IndexProcessor implements Runnable { private static final Logger LOGGER = LoggerFactory.getLogger(IndexProcessor.class); private final CountDownLatch countdownlatch; public IndexProcessor(CountDownLatch countdownlatch) { this.countdownlatch = countdownlatch; } public void run() { try { while (!countdownlatch.await(15000, TimeUnit.MILLISECONDS)) { LOGGER.debug("Processing..."); } } catch (InterruptedException e) { LOGGER.error("Exception", e); run = false; } } } 

Wenn Sie die Ausführung des anderen Threads beenden möchten, führen Sie countDown für CountDownLatch und verknüpfen Sie den Thread mit dem Hauptthread:

 public class SearchEngineContextListener implements ServletContextListener { private static final Logger LOGGER = LoggerFactory.getLogger(SearchEngineContextListener.class); private Thread thread = null; private IndexProcessor runnable = null; private CountDownLatch countdownLatch = null; @Override public void contextInitialized(ServletContextEvent event) { countdownLatch = new CountDownLatch(1); Thread thread = new Thread(new IndexProcessor(countdownLatch)); LOGGER.debug("Starting thread: " + thread); thread.start(); LOGGER.debug("Background process successfully started."); } @Override public void contextDestroyed(ServletContextEvent event) { LOGGER.debug("Stopping thread: " + thread); if (countdownLatch != null) { countdownLatch.countDown(); } if (thread != null) { try { thread.join(); } catch (InterruptedException e) { LOGGER.error("Exception", e); } LOGGER.debug("Thread successfully stopped."); } } } 

Wenn wir JDK 1.0 verwenden, können wir die veraltete Methode stop () von Thread aufrufen, um sie zu beenden. Die Verwendung von stop () ist unglaublich gefährlich, da es den Thread selbst dann umbringt, wenn es sich um etwas Wichtiges handelt. Es gibt keine Möglichkeit, sich selbst zu schützen. Wenn Sie Code finden, der stop () verwendet, sollten Sie die Stirn runzeln.

Wie können wir einen Thread sauber herunterfahren?

In Java ist das Starten von Threads einfach, aber das Herunterfahren erfordert viel Aufmerksamkeit und Anstrengungen.

Hier ist, wie es in Java entworfen wird. Es gibt ein Flag namens Interrupt Status Flag in jedem Java-Thread, das wir von außen setzen können, dh Eltern- oder Haupt-Thread. Und der Thread überprüft es gelegentlich und stoppt die Ausführung. Freiwillig..!! Hier ist, wie:

 Thread loop = new Thread(new Runnable() { @Override public void run() { while (true) { if (Thread.interrupted()) { break; } // Continue to do nothing } }}); loop.start(); loop.interrupt(); 

Quelle: Wie man Thread in Java stoppt | Multi-Threading

Einige ergänzende Informationen. Flag und Interrupt werden im Java-Dokument vorgeschlagen.

https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

 private volatile Thread blinker; public void stop() { blinker = null; } public void run() { Thread thisThread = Thread.currentThread(); while (blinker == thisThread) { try { Thread.sleep(interval); } catch (InterruptedException e){ } repaint(); } } 

Verwenden Thread.interrupt für einen Thread, der lange Zeit (z. B. für die Eingabe) wartet, Thread.interrupt

 public void stop() { Thread moribund = waiter; waiter = null; moribund.interrupt(); } 

Ich habe die Unterbrechung nicht bekommen, um in Android zu arbeiten, also habe ich diese Methode verwendet, funktioniert perfekt:

 boolean shouldCheckUpdates = true; private void startupCheckForUpdatesEveryFewSeconds() { threadCheckChat = new Thread(new CheckUpdates()); threadCheckChat.start(); } private class CheckUpdates implements Runnable{ public void run() { while (shouldCheckUpdates){ System.out.println("Do your thing here"); } } } public void stop(){ shouldCheckUpdates = false; }