Wie stoppe ich den Thread in Java richtig?

Lesezeit: 12 Minuten

Wie stoppe ich den Thread in Java richtig
Paulius Matulionis

Ich brauche eine Lösung, um den Thread in Java richtig zu stoppen.

Ich habe IndexProcessorKlasse, 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 Klasse, 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, erhalte ich die Ausnahme in meiner IndexProcessor-Klasse:

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 verwende JDK 1.6. Die Frage ist also:

Wie kann ich den Thread stoppen und keine Ausnahmen auslösen?

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

  • Das Beenden eines Threads auf halbem Weg führt immer zu einer Ausnahme. Wenn es sich um ein normales Verhalten handelt, können Sie es einfach abfangen und ignorieren InterruptedException. Das ist, was ich denke, aber ich frage mich auch, wie der Standardweg ist.

    – nhahtdh

    9. Juni 2012 um 14:18 Uhr


  • Ich habe Threads nicht sehr oft verwendet, daher bin ich ziemlich neu in Threads, daher weiß ich nicht, ob es normales Verhalten ist, die Ausnahme zu ignorieren. Deshalb frage ich.

    – Paulius Matulionis

    9. Juni 2012 um 14:23 Uhr


  • In vielen Fällen ist es normales Verhalten, die Ausnahme zu ignorieren und die Methodenverarbeitung zu beenden. Siehe meine Antwort unten, warum dies besser ist als ein Flag-basierter Ansatz.

    – Matt

    9. Juni 2012 um 16:29 Uhr

  • Eine nette Erklärung von B. Goetz bzgl InterruptedException finden Sie unter ibm.com/developerworks/library/j-jtp05236.

    – Daniel

    30. Dezember 2014 um 19:33 Uhr

  • die InterruptedException ist kein Problem, Ihr einziges Problem im geposteten Code ist, dass Sie es nicht als Fehler protokollieren sollten, es gibt wirklich keinen zwingenden Grund, es als alles zu protokollieren, außer als Debug, nur um zu demonstrieren, dass es passiert ist, falls Sie interessiert sind . die gewählte Antwort ist unglücklich, weil sie es nicht erlaubt, kurze Anrufe auf Anrufe wie Schlafen und Warten zu unterbrechen.

    – Nathan Hughes

    13. Februar 2015 um 20:57 Uhr


1646630837 492 Wie stoppe ich den Thread in Java richtig
Matt

Verwenden Thread.interrupt() ist ein durchaus akzeptabler Weg, dies zu tun. Tatsächlich ist es wahrscheinlich einer Flagge vorzuziehen, wie oben vorgeschlagen. Der Grund dafür ist, dass, wenn Sie sich in einem unterbrechbaren blockierenden Anruf befinden (z Thread.sleep oder java.nio Channel-Operationen verwenden), können Sie tatsächlich sofort aus diesen ausbrechen.

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

In diesem Fall unterbricht ein Thread, wenn er unterbrochen wird, die E / A nicht, Sie können dies jedoch problemlos routinemäßig in Ihrem Code tun (und Sie sollten dies an strategischen Punkten tun, an denen 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 zu Thread.interrupt() ist, dass Sie sofort aus unterbrechbaren Anrufen ausbrechen können, was Sie mit dem Flag-Ansatz nicht tun können.

  • +1 – Thread.interupt() ist bestimmt vorzuziehen, dasselbe mit einem Ad-hoc-Flag zu implementieren.

    – Stefan C

    6. April 2013 um 4:57 Uhr

  • Ich denke auch, dass dies der perfekte und effiziente Weg ist, dies zu tun. +1

    – RoboAlex

    10. April 2013 um 15:27 Uhr

  • Es gibt einen kleinen Tippfehler im Code, Thread.currentThread() hat keine Klammern.

    – Vlad V

    12. Dezember 2013 um 0:13 Uhr

  • Tatsächlich ist es nicht vorzuziehen, ein Flag zu verwenden, da jemand anderes, der mit dem Thread in Kontakt kommt, ihn von woanders unterbrechen kann, was dazu führt, dass er stoppt und sehr schwer zu debuggen ist. Verwenden Sie immer auch eine Flagge.

    – JohnyTex

    18. Januar 2016 um 8:10 Uhr

  • In diesem speziellen Fall anrufen interrupt() kann in Ordnung sein, ist es aber in vielen anderen Fällen nicht (z. B. wenn eine Ressource geschlossen werden muss). Wenn jemand die innere Funktionsweise der Schleife ändert, müssen Sie daran denken, dies zu ändern interrupt() auf den booleschen Weg. Ich würde von Anfang an den sicheren Weg gehen und die Flagge benutzen.

    – m0skit0

    23. September 2016 um 9:00 Uhr


1646630838 834 Wie stoppe ich den Thread in Java richtig
DrYap

In dem IndexProcessor Klasse benötigen Sie eine Möglichkeit, ein Flag zu setzen, das den Thread darüber informiert, dass er beendet werden muss, ähnlich wie bei der Variablen run die Sie gerade im Klassenbereich verwendet haben.

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

Stellen Sie sicher, dass das Flag Thread-sicher ist, indem Sie eine flüchtige Variable verwenden oder 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.");
        }
    }
}

  • Ich habe genau dasselbe getan, wie Sie die Beispiele in Ihrer Antwort gegeben haben, bevor ich nachgesehen habe, dass Sie sie bearbeitet haben. Gute Antwort! Danke, jetzt funktioniert alles einwandfrei 🙂

    – Paulius Matulionis

    9. Juni 2012 um 14:39 Uhr

  • Was ist, wenn die Thread-Logik komplex ist und viele Methoden anderer Klassen aufruft? Es ist nicht möglich, das boolesche Flag überall zu überprüfen. Was ist dann zu tun?

    – Soterisch

    9. Juni 2012 um 14:53 Uhr

  • Sie müssten das Codedesign so ändern, dass Sie es so erstellen, dass ein Signal an das Runnable dazu führt, dass der Thread beendet wird. Die meisten Anwendungen haben diese Schleife in der run-Methode, sodass das Problem normalerweise nicht besteht.

    – DrYap

    9. Juni 2012 um 14:57 Uhr


  • Was passiert, wenn die join()-Anweisung eine InterruptedException auslöst?

    – Benzaita

    19. November 2014 um 9:53 Uhr

  • Abgewertet für die Verbreitung schlechter Ratschläge. Der handgerollte Flag-Ansatz bedeutet, dass die Anwendung warten muss, bis der Schlaf beendet ist, wo eine Unterbrechung den Schlaf verkürzen würde. Es wäre einfach, dies zu ändern, um Thread#interrupt zu verwenden.

    – Nathan Hughes

    9. Oktober 2015 um 12:28 Uhr

Wie stoppe ich den Thread in Java richtig
Hamster aus Dunkel

Einfache Antwort: Sie können einen Thread intern auf zwei gängige Arten stoppen:

  • Die Run-Methode trifft auf eine Return-Subroutine.
  • Die Run-Methode wird beendet und implizit zurückgegeben.

Sie können Threads auch EXTERN stoppen:

  • Anruf system.exit (Dies tötet Ihren gesamten Prozess)
  • Rufen Sie das Thread-Objekt auf interrupt() Methode *
  • Sehen Sie, ob der Thread eine implementierte Methode hat, die so klingt, als würde sie funktionieren (wie z kill() oder stop())

*: Die Erwartung ist, dass dies einen Thread stoppen soll. Was der Thread in diesem Fall tatsächlich tut, hängt jedoch vollständig davon ab, was der Entwickler geschrieben hat, als er die Thread-Implementierung erstellt hat.

Ein allgemeines Muster, das Sie bei Implementierungen von run-Methoden sehen, ist a while(boolean){}wobei der boolesche Wert normalerweise etwas Benanntes ist isRunninges ist eine Mitgliedsvariable seiner Thread-Klasse, es ist flüchtig und normalerweise für andere Threads über eine Art Setter-Methode zugänglich, z kill() { isRunnable=false; }. Diese Subroutinen sind nett, weil sie es dem Thread ermöglichen, alle Ressourcen, die er hält, freizugeben, bevor er beendet wird.

  • “Diese Subroutinen sind nett, weil sie es dem Thread ermöglichen, alle Ressourcen, die er enthält, freizugeben, bevor er beendet wird.” Ich verstehe nicht. Sie können die gehaltenen Ressourcen eines Threads problemlos bereinigen, indem Sie den “offiziellen” Unterbrechungsstatus verwenden. Überprüfen Sie es einfach mit Thread.currentThread().isInterrupted() oder Thread.interrupted() (je nachdem, was Ihren Anforderungen entspricht) oder fangen Sie die InterruptedException ab und bereinigen Sie sie. Wo ist das Problem?

    – Franz D.

    19. August 2018 um 19:29 Uhr

  • Ich konnte nicht verstehen, warum die Flag-Methode funktioniert, weil ich nicht verstanden hatte, dass sie aufhört, wenn der Lauf zurückkehrt !!! Das war so einfach, sehr geehrter Herr, danke für den Hinweis, niemand hatte dies ausdrücklich getan.

    – thahgr

    2. Januar 2019 um 14:51 Uhr

Sie sollten Threads immer beenden, indem Sie ein Flag im aktivieren run() Schleife (falls vorhanden).

Dein Thread sollte so 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 kannst du den Thread per Anruf beenden thread.stopExecuting(). Auf diese Weise wird der Thread sauber beendet, aber dies dauert bis zu 15 Sekunden (aufgrund Ihres Schlafs). Sie können immer noch thread.interrupt() aufrufen, wenn es wirklich dringend ist – aber der bevorzugte Weg sollte immer das Überprüfen des Flags sein.

Um 15 Sekunden Wartezeit zu vermeiden, können Sie den Schlaf wie folgt aufteilen:

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

            LOGGER.debug("Processing");
        } catch (InterruptedException e) {
        ...

Normalerweise wird ein Thread beendet, wenn er unterbrochen wird. Warum also nicht den nativen booleschen Wert verwenden? Versuchen Sie isInterrupted():

Thread t = new Thread(new Runnable(){
        @Override
        public void run() {
            while(!Thread.currentThread().isInterrupted()){
                // do stuff         
            }   
        }});
    t.start();

    // Sleep a second, and then interrupt
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {}
    t.interrupt();

ref- Wie kann ich einen Thread beenden? ohne stop();

  • Der unterbrochene Faden IST NICHT beendet, es wird nur unterbrochen. Wenn der Thread während in unterbrochen wird sleep()dann wirft es InterruptedException die dein Thread auffangen und eventuell selbst beenden oder vielleicht einfach weiterverarbeiten soll. isInterrutped wird sein true NUR wenn der Thread nicht drin wäre sleep oder wait (es lief tatsächlich) und dies kann Ihnen einen Hinweis darauf geben, dass der Thread unterbrochen wurde. Mit anderen Worten: Wenn Sie setzen sleep() in deiner // do stuff Linie, dann t.interrupt() wird es nicht beenden (zu 99,9 % wird es das nicht).

    – Cromax

    29. Juni 2020 um 22:40 Uhr

  • Der erste Satz der Antwort sollte lauten: “Normalerweise besteht das Ziel einer Unterbrechung darin, einen Thread zu beenden.” Um zu verdeutlichen, was @Cromax gesagt hat, falls dies der Fall ist while Bedingung wird die einzige Möglichkeit sein, den Thread zu stoppen, dann jede catch (InterruptedException) Block muss anrufen Thread.currentThread().interrupt(); um den unterbrochenen Status für die aufrechtzuerhalten while Zustand. Aber je nach Arbeit kann es sicher sein, diese zu verwenden catch Blöcke zu break; frühzeitig aus der Schleife oder sogar sicher, weitere hinzuzufügen .isInterrupted() Kontrollen im gesamten Schleifenkörper.

    – AndreasF

    26. Februar 2021 um 6:10 Uhr


Zum Synchronisieren von Threads bevorzuge ich die Verwendung von CountDownLatch Dies hilft Threads zu warten, bis der ausgeführte Prozess abgeschlossen ist. In diesem Fall wird die Arbeiterklasse mit a eingerichtet CountDownLatch Instanz mit einer bestimmten Anzahl. Ein Anruf bei await -Methode wird blockiert, bis die aktuelle Anzahl aufgrund von Aufrufen von null erreicht countDown Methode oder das eingestellte Timeout erreicht ist. Dieser Ansatz ermöglicht es, einen Thread sofort zu unterbrechen, ohne auf das Verstreichen der angegebenen 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 auf dem aus CountDownLatch und join der Thread zum 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.");
        } 
    }
}

  • Der unterbrochene Faden IST NICHT beendet, es wird nur unterbrochen. Wenn der Thread während in unterbrochen wird sleep()dann wirft es InterruptedException die dein Thread auffangen und eventuell selbst beenden oder vielleicht einfach weiterverarbeiten soll. isInterrutped wird sein true NUR wenn der Thread nicht drin wäre sleep oder wait (es lief tatsächlich) und dies kann Ihnen einen Hinweis darauf geben, dass der Thread unterbrochen wurde. Mit anderen Worten: Wenn Sie setzen sleep() in deiner // do stuff Linie, dann t.interrupt() wird es nicht beenden (zu 99,9 % wird es das nicht).

    – Cromax

    29. Juni 2020 um 22:40 Uhr

  • Der erste Satz der Antwort sollte lauten: “Normalerweise besteht das Ziel einer Unterbrechung darin, einen Thread zu beenden.” Um zu verdeutlichen, was @Cromax gesagt hat, falls dies der Fall ist while Bedingung wird die einzige Möglichkeit sein, den Thread zu stoppen, dann jede catch (InterruptedException) Block muss anrufen Thread.currentThread().interrupt(); um den unterbrochenen Status für die aufrechtzuerhalten while Zustand. Aber je nach Arbeit kann es sicher sein, diese zu verwenden catch Blöcke zu break; frühzeitig aus der Schleife oder sogar sicher, weitere hinzuzufügen .isInterrupted() Kontrollen im gesamten Schleifenkörper.

    – AndreasF

    26. Februar 2021 um 6:10 Uhr


1646630839 219 Wie stoppe ich den Thread in Java richtig
Feng

Einige ergänzende Informationen. Sowohl Flag als auch 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 Sie für einen Thread, der lange wartet (z. B. auf Eingaben). Thread.interrupt

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

  • Ignorieren Sie niemals eine InterruptedException. Dies bedeutet, dass ein anderer Code Ihren Thread explizit zum Beenden auffordert. Ein Thread, der diese Anfrage ignoriert, ist ein Rogue-Thread. Die korrekte Behandlung einer InterruptedException besteht darin, die Schleife zu verlassen.

    – VGR

    21. März 2018 um 19:54 Uhr

963150cookie-checkWie stoppe ich den Thread in Java richtig?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy