J’ai besoin d’une solution pour arrêter correctement le thread en Java.
J’ai la classe IndexProcessor
qui implémente l’interface Runnable:
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; } } } }
Et j’ai la classe ServletContextListener
qui démarre et arrête le thread:
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."); } } }
Mais quand j’arrête Tomcat, j’obtiens l’exception dans ma classe IndexProcessor:
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)
J’utilise JDK 1.6. Donc la question est:
Comment puis-je arrêter le thread et ne pas lancer d’exceptions?
PS je ne veux pas utiliser .stop();
méthode car elle est obsolète.
Dans la classe IndexProcessor
, vous avez besoin d’un moyen de définir un indicateur qui informe le thread qu’il devra terminer, de la même manière que la variable utilisée dans la scope de la classe.
Lorsque vous souhaitez arrêter le thread, vous définissez cet indicateur et appelez join()
sur le thread et attendez qu’il se termine.
Assurez-vous que l’indicateur est thread-safe en utilisant une variable volatile ou en utilisant les méthodes getter et setter synchronisées avec la variable utilisée comme indicateur.
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; } } } }
Puis dans 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."); } } }
Utiliser Thread.interrupt()
est une manière tout à fait acceptable de le faire. En fait, il est probablement préférable à un drapeau comme suggéré ci-dessus. La raison en est que si vous effectuez un appel bloquant (comme Thread.sleep
ou en utilisant les opérations du canal java.nio), vous pourrez en sortir immédiatement.
Si vous utilisez un drapeau, vous devez attendre que l’opération de blocage se termine et vous pouvez alors vérifier votre drapeau. Dans certains cas, vous devez le faire de toute façon, par exemple en utilisant un InputStream
/ OutputStream
standard qui ne peut pas être interrompu.
Dans ce cas, lorsqu’un thread est interrompu, il n’interrompt pas l’IO, mais vous pouvez facilement le faire régulièrement dans votre code (et vous devez le faire à des endroits stratégiques où vous pouvez arrêter et nettoyer en toute sécurité)
if (Thread.currentThread().isInterrupted()) { // cleanup and stop execution // for example a break in a loop }
Comme je l’ai dit, le principal avantage de Thread.interrupt()
est que vous pouvez immédiatement interrompre les appels interruptibles, ce que vous ne pouvez pas faire avec l’approche des drapeaux.
Réponse simple: Vous pouvez arrêter un thread INTERNE de l’une des deux manières suivantes:
Vous pouvez également arrêter les threads EXTERNALLY:
system.exit
(cela tue tout votre processus) interrupt()
l’object thread * kill()
ou stop()
) *: On s’attend à ce que cela soit censé arrêter un thread. Cependant, ce que le thread fait réellement lorsque cela se produit dépend entièrement de ce que le développeur a écrit lorsqu’il a créé l’implémentation du thread.
Un motif courant que vous voyez avec les implémentations de méthodes d’exécution est un while(boolean){}
, où le booléen est généralement quelque chose nommé isRunning
, c’est une variable membre de sa classe de thread, volatile et généralement accessible par une méthode setter de sortes, par exemple kill() { isRunnable=false; }
kill() { isRunnable=false; }
. Ces sous-routines sont agréables car elles permettent au thread de libérer toutes les ressources qu’il détient avant de se terminer.
Vous devez toujours terminer les threads en vérifiant un indicateur dans la boucle run()
cas échéant.
Votre thread devrait ressembler à ceci:
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; } }
Ensuite, vous pouvez terminer le thread en appelant thread.stopExecuting()
. De cette façon, le thread est terminé, mais cela prend 15 secondes (à cause de votre sumil). Vous pouvez toujours appeler thread.interrupt () si c’est vraiment urgent – mais la manière préférée devrait toujours vérifier le drapeau.
Pour éviter d’attendre 15 secondes, vous pouvez diviser le sumil comme ceci:
... try { LOGGER.debug("Sleeping..."); for (int i = 0; (i < 150) && this.execute; i++) { Thread.sleep((long) 100); } LOGGER.debug("Processing"); } catch (InterruptedException e) { ...
Pour synchroniser les threads, je préfère utiliser CountDownLatch
qui aide les threads à attendre que le processus soit terminé. Dans ce cas, la classe de travail est configurée avec une instance CountDownLatch
avec un nombre donné. Une méthode d’appel à l’ await
sera bloquée jusqu’à ce que le nombre actuel atteigne zéro en raison des countDown
méthode countDown
ou countDown
le délai d’attente est atteint. Cette approche permet d’interrompre instantanément un thread sans avoir à attendre que le temps d’attente spécifié s’écoule:
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; } } }
Lorsque vous souhaitez terminer l’exécution de l’autre thread, exécutez countDown sur CountDownLatch
et join
le thread au thread principal:
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."); } } }
Si nous utilisons JDK 1.0, nous pouvons appeler la méthode obsolète Thread stop () pour la terminer. Utiliser stop () est extrêmement dangereux, car il tuera votre thread même s’il est au centre de quelque chose d’important. Il n’y a aucun moyen de vous protéger, donc si vous repérez le code qui utilise stop (), vous devriez froncer les sourcils.
Comment arrêter un thread proprement?
En Java, démarrer un thread est facile, mais les fermer nécessite beaucoup d’attention et d’efforts.
Voici comment il est conçu en Java. Il y a un drapeau appelé indicateur d’état d’Interrupt dans chaque thread Java que nous pouvons définir de l’extérieur, c’est-à-dire le thread principal ou parent. Et le thread peut le vérifier de temps en temps et arrête son exécution. Volontairement..!! Voici comment:
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: Comment arrêter le thread en java | MultiThreading
Quelques informations supplémentaires. Les indicateurs et les interruptions sont suggérés dans le document Java.
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(); } }
Pour un thread qui attend de longues périodes (par exemple, pour les entrées), utilisez Thread.interrupt
public void stop() { Thread moribund = waiter; waiter = null; moribund.interrupt(); }
Je n’ai pas eu l’interruption pour fonctionner dans Android, donc j’ai utilisé cette méthode, fonctionne parfaitement:
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; }