Hoe stop je de thread op de juiste manier in Java?

Ik heb een oplossing nodig om de thread in Java correct te stoppen.

Ik heb de klasse IndexProcessordie de Runnable-interface implementeert:

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;
            }
        }
    }
}

En ik heb de klasse ServletContextListenerdie de thread start en stopt:

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

Maar als ik Tomcat afsluit, krijg ik de uitzondering in mijn 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)

Ik gebruik JDK 1.6. Dus de vraag is:

Hoe kan ik de thread stoppen en geen uitzonderingen maken?

P.S.ik wil de methode .stop();niet gebruiken omdat deze verouderd is.


Antwoord 1, autoriteit 100%

In de klasse IndexProcessorheb je een manier nodig om een vlag in te stellen die de thread informeert dat deze moet worden beëindigd, vergelijkbaar met de variabele rundie je zojuist hebt gebruikt in het klassebereik.

Als u de thread wilt stoppen, stelt u deze vlag in en roept u join()aan op de thread en wacht u tot deze is afgelopen.

Zorg ervoor dat de vlag threadveilig is door een vluchtige variabele te gebruiken of door getter- en setter-methoden te gebruiken die zijn gesynchroniseerd met de variabele die als vlag wordt gebruikt.

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;
            }
        }
    }
}

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

Antwoord 2, autoriteit 98%

Het gebruik van Thread.interrupt()is een perfect acceptabele manier om dit te doen. In feite heeft het waarschijnlijk de voorkeur boven een vlag zoals hierboven gesuggereerd. De reden hiervoor is dat als je een onderbreekbaar blokkeergesprek voert (zoals Thread.sleepof java.nio Channel-bewerkingen gebruikt), je daar meteen uit kunt komen.

Als u een vlag gebruikt, moet u wachten tot de blokkering is voltooid en kunt u uw vlag controleren. In sommige gevallen moet u dit toch doen, zoals het gebruik van standaard InputStream/OutputStreamdie niet kunnen worden onderbroken.

In dat geval, wanneer een thread wordt onderbroken, zal deze de IO niet onderbreken, maar u kunt dit eenvoudig routinematig in uw code doen (en u moet dit doen op strategische punten waar u veilig kunt stoppen en opschonen)

p>

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

Zoals ik al zei, het belangrijkste voordeel van Thread.interrupt()is dat je onmiddellijk kunt breken met onderbreekbare oproepen, wat je niet kunt doen met de vlagbenadering.


Antwoord 3, autoriteit 15%

Eenvoudig antwoord:
U kunt een discussie INTERN op twee manieren stoppen:

  • De run-methode treft een return-subroutine.
  • Run-methode eindigt en keert impliciet terug.

Je kunt discussielijnen ook EXTERN stoppen:

  • Bel system.exit(dit stopt je hele proces)
  • Roep de interrupt()-methode van het thread-object *
  • Kijk of de thread een geïmplementeerde methode heeft die klinkt alsof het zou werken (zoals kill()of stop())

*: De verwachting is dat dit een thread zou moeten stoppen. Wat de thread daadwerkelijk doet wanneer dit gebeurt, hangt echter helemaal af van wat de ontwikkelaar schreef toen ze de thread-implementatie maakten.

Een veelvoorkomend patroon dat u ziet bij implementaties van runmethodes is een while(boolean){}, waarbij de boolean typisch iets is met de naam isRunning, het is een lidvariabele van zijn thread-klasse, het is vluchtig en meestal toegankelijk voor andere threads via een soort setter-methode, bijv kill() { isRunnable=false; }. Deze subroutines zijn leuk omdat ze de thread toestaan om alle bronnen vrij te geven die het bevat voordat het wordt beëindigd.


Antwoord 4, autoriteit 5%

Je moet discussielijnen altijd beëindigen door een vlag in de run()-lus (indien aanwezig) aan te vinken.

Uw thread zou er als volgt uit moeten zien:

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;
    }
}

Vervolgens kunt u de thread beëindigen door thread.stopExecuting()aan te roepen. Op die manier wordt de draad schoon beëindigd, maar dit duurt tot 15 seconden (vanwege je slaap).
Je kunt nog steeds thread.interrupt() aanroepen als het echt dringend is – maar de beste manier zou altijd het controleren van de vlag moeten zijn.

Om te voorkomen dat u 15 seconden moet wachten, kunt u de slaap als volgt opsplitsen:

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

Antwoord 5, autoriteit 4%

Normaal gesproken wordt een thread beëindigd wanneer deze wordt onderbroken. Dus waarom niet de native boolean gebruiken? Probeer 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- Hoe mag ik een draadje doden? zonder stop();


Antwoord 6, autoriteit 3%

Voor het synchroniseren van threads gebruik ik liever CountDownLatch, waardoor threads kunnen wachten tot het proces dat wordt uitgevoerd, is voltooid. In dit geval wordt de worker-klasse ingesteld met een CountDownLatch-instantie met een bepaald aantal. Een aanroep van de await-methode wordt geblokkeerd totdat de huidige telling nul bereikt vanwege het aanroepen van de countDown-methode of totdat de ingestelde time-out is bereikt. Deze aanpak maakt het mogelijk om een thread onmiddellijk te onderbreken zonder te hoeven wachten tot de opgegeven wachttijd is verstreken:

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;
        }
    }
}

Als je de uitvoering van de andere thread wilt beëindigen, voer dan countDown uit op de CountDownLatchen joinde thread toe aan de hoofdthread:

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

Antwoord 7, autoriteit 2%

Enkele aanvullende informatie.
Zowel vlag als interrupt worden voorgesteld in het Java-document.

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();
    }
}

Voor een thread die lange tijd wacht (bijvoorbeeld voor invoer), gebruik Thread.interrupt

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

Antwoord 8

Ik kreeg de interrupt niet werkend in Android, dus ik heb deze methode gebruikt, werkt perfect:

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;
 }

Other episodes