web-dev-qa-db-ger.com

Wie stoppe ich den Thread in Java richtig?

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

Ich habe IndexProcessorclass, 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.");
        }
    }
}

Beim Herunterfahren von Tomcat tritt jedoch die Ausnahme in meiner IndexProcessor-Klasse auf:

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 anhalten und keine Ausnahmen werfen?

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

253

In der Klasse IndexProcessor müssen Sie ein Flag setzen, das den Thread darüber informiert, dass er beendet werden muss, ähnlich der Variablen run, die Sie gerade im Klassenbereich verwendet haben.

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

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

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.");
        }
    }
}
163
DrYap

Die Verwendung von Thread.interrupt() ist hierfür eine durchaus akzeptable Methode. Tatsächlich ist es wahrscheinlich einer Flagge vorzuziehen, wie oben vorgeschlagen. Der Grund dafür ist, dass Sie in der Lage sind, einen unterbrechbaren Blockierungsanruf (wie Thread.sleep oder die Verwendung von Java.nio Channel-Vorgängen) sofort zu beenden.

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

In diesem Fall unterbricht ein Thread die E/A nicht. Sie können dies jedoch problemlos routinemäßig in Ihrem Code ausführen (und dies an strategischen Punkten, an denen Sie sicher stoppen und bereinigen können).

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

Wie gesagt, der Hauptvorteil von Thread.interrupt() ist, dass Sie sofort aus unterbrechbaren Anrufen ausbrechen können, was mit dem Flag-Ansatz nicht möglich ist.

282
Matt

Einfache Antwort: Sie können einen Thread auf zwei übliche Arten INTERN stoppen:

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

Sie können Threads auch EXTERN stoppen:

  • Rufe system.exit auf (dies bricht deinen gesamten Prozess ab)
  • Rufen Sie die Methode interrupt() des Thread-Objekts auf *
  • Überprüfen Sie, ob für den Thread eine Methode implementiert ist, die nach Funktionsweise klingt (z. B. kill() oder stop()).

*: Die Erwartung ist, dass dies einen Thread stoppen soll. Was der Thread in diesem Fall jedoch tatsächlich tut, hängt ganz davon ab, was der Entwickler bei der Erstellung der Thread-Implementierung geschrieben hat.

Ein verbreitetes Muster, das Sie bei Implementierungen von Ausführungsmethoden sehen, ist while(boolean){}, wobei der Boolesche Wert normalerweise isRunning heißt, eine Mitgliedsvariable seiner Thread-Klasse ist, flüchtig ist und in der Regel von anderen Threads mit einer Art Setter-Methode aufgerufen werden kann , z.B kill() { isRunnable=false; }. Diese Subroutinen sind nett, weil sie es dem Thread ermöglichen, alle Ressourcen freizugeben, die er vor dem Beenden hält.

25
hamsterofdark

Sie sollten Threads immer beenden, indem Sie ein Flag in der run() -Schleife markieren (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 können Sie den Thread beenden, indem Sie thread.stopExecuting() aufrufen. Auf diese Weise wird der Thread sauber beendet, dies dauert jedoch (aufgrund Ihres Schlafes) bis zu 15 Sekunden. Sie können thread.interrupt () immer noch aufrufen, wenn es wirklich dringend ist - aber der bevorzugte Weg sollte immer darin bestehen, das Flag zu überprüfen.

Um nicht 15 Sekunden warten zu müssen, 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) {
        ...
9
Chris

Wenn wir JDK 1.0 verwenden, können wir die veraltete Methode stop () von Thread aufrufen, um es zu beenden. Die Verwendung von stop () ist unglaublich gefährlich, da dies Ihren Thread selbst dann zum Erliegen bringt, wenn er sich mitten in etwas Wichtigem befindet. Es gibt keine Möglichkeit, sich zu schützen. Wenn Sie also Code erkennen, der stop () verwendet, sollten Sie die Stirn runzeln.

Wie fahren wir einen Thread sauber herunter?

In Java ist das Starten eines Threads einfach, das Herunterfahren erfordert jedoch viel Aufmerksamkeit und Aufwand.

So ist es in Java aufgebaut. In jedem Java Thread, den wir von außen setzen können, gibt es ein Flag namens Interrupt status flag, d. H. Eltern- oder Haupt-Thread. Und der Thread kann es gelegentlich überprüfen und seine Ausführung stoppen. 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();

source: So stoppen Sie den Thread in Java | MultiThreading

6
riteeka

Zum Synchronisieren von Threads bevorzuge ich die Verwendung von CountDownLatch, wodurch Threads warten können, bis der Prozess abgeschlossen ist. In diesem Fall wird die Worker-Klasse mit einer CountDownLatch -Instanz mit einer bestimmten Anzahl eingerichtet. Ein Aufruf der Methode await wird blockiert, bis der aktuelle Zählerstand aufgrund von Aufrufen der Methode countDown oder der festgelegten Zeitüberschreitung Null erreicht. Mit diesem Ansatz können Sie einen Thread sofort 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 join für den Thread zum Haupt-Thread aus:

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.");
        } 
    }
}
5

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 längere Zeit wartet (z. B. auf Eingaben), Thread.interrupt

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

Ich habe den Interrupt in Android nicht zum Laufen gebracht, daher 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;
 }
2
Mindborg

In der Regel 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 töten? ohne stop ();