web-dev-qa-db-fra.com

Comment arrêter correctement le thread en Java?

J'ai besoin d'une solution pour arrêter correctement le fil de discussion en Java.

J'ai IndexProcessorclass 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 lorsque j'arrête Tomcat, j'obtiens une 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. La question est donc:

Comment puis-je arrêter le fil et ne pas lancer d'exceptions?

P.S. Je ne souhaite pas utiliser la méthode .stop(); car elle est obsolète.

252

Dans la classe IndexProcessorname__, vous avez besoin d'un moyen de définir un indicateur informant le thread qu'il doit terminer, similaire à la variable runque vous avez utilisée uniquement dans la portée de la classe.

Lorsque vous souhaitez arrêter le fil, définissez cet indicateur, appelez join() sur le fil et attendez la fin.

Assurez-vous que l'indicateur est thread-safe en utilisant une variable volatile ou en utilisant des méthodes getter et setter synchronisées avec la variable utilisée en tant qu'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 SearchEngineContextListenername__:

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

Utiliser Thread.interrupt() est un moyen parfaitement acceptable de le faire. En fait, c'est probablement préférable à un drapeau comme suggéré ci-dessus. La raison en est que si vous êtes dans un appel bloquant pouvant être interrompu (comme Thread.sleep ou en utilisant les opérations du canal Java.nio), vous pourrez réellement vous échapper tout de suite.

Si vous utilisez un indicateur, vous devez attendre la fin de l'opération de blocage pour pouvoir ensuite vérifier votre indicateur. Dans certains cas, vous devez quand même le faire, par exemple en utilisant le nom standard InputStreamname __/OutputStreamqui ne peut pas être interrompu.

Dans ce cas, lorsqu'un thread est interrompu, il n'interrompra pas l'IO. Toutefois, vous pouvez le faire facilement dans votre code (et vous devriez le faire à des points stratégiques où vous pouvez vous 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 qu’il est possible de rompre immédiatement les appels pouvant être interrompus, ce que vous ne pouvez pas utiliser avec l’approche en drapeau.

279
Matt

Réponse simple: Vous pouvez arrêter un fil INTERNE de deux manières courantes:

  • La méthode run frappe un sous-programme de retour.
  • La méthode Run se termine et retourne implicitement.

Vous pouvez également arrêter les discussions EXTERNALEMENT:

  • Appelez system.exit (cela tue tout votre processus)
  • Appelez la méthode interrupt() de l'objet thread *
  • Voir si le thread a une méthode implémentée qui semble fonctionner (comme kill() ou stop())

*: On s'attend à ce que cela soit censé arrêter un fil. Cependant, ce que fait le fil lorsque cela se produit est entièrement conforme à ce que le développeur a écrit lors de la création de son implémentation.

Un modèle courant que vous voyez avec les implémentations de la méthode run est un while(boolean){}, où le booléen est généralement nommé isRunning, une variable membre de sa classe de thread, il est volatile et est généralement accessible par d’autres threads à l’aide d’une méthode de définition, par exemple. kill() { isRunnable=false; }. Ces sous-routines sont bien car elles permettent au thread de libérer toutes les ressources qu’il détient avant de se terminer.

25
hamsterofdark

Vous devez toujours terminer les threads en cochant un indicateur dans la boucle run() (le cas échéant).

Votre fil 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 fil en appelant thread.stopExecuting(). De cette façon, le fil se termine propre, mais cela prend jusqu'à 15 secondes (à cause de votre sommeil). Vous pouvez toujours appeler thread.interrupt () si c'est vraiment urgent - mais le moyen préféré devrait toujours être de vérifier l'indicateur.

Pour éviter d'attendre 15 secondes, vous pouvez répartir le sommeil de la manière suivante:

        ...
        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

Si nous utilisons JDK 1.0, nous pouvons appeler la méthode obsolète de Thread stop () pour le terminer. Utiliser stop () est incroyablement dangereux, car cela tue votre thread, même s'il se trouve au milieu de quelque chose d'important. Il n'y a aucun moyen de vous protéger, donc si vous remarquez du code qui utilise stop (), vous devriez froncer les sourcils.

Comment pouvons-nous arrêter un thread proprement?

En Java, démarrer un thread est facile, mais sa fermeture nécessite beaucoup d’attention et d’efforts.

Voici comment cela est conçu en Java. Il existe un indicateur appelé indicateur de statut d'interruption dans chaque thread Java que nous pouvons définir à partir du thread extérieur ou principal. Et le thread peut le vérifier de temps en temps et arrêter 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 fil en Java | MultiThreading

6
riteeka

Pour la synchronisation des threads, je préfère utiliser CountDownLatchname__, qui permet aux threads d’attendre la fin du processus. Dans ce cas, la classe de travail est configurée avec une instance CountDownLatchavec un nombre donné. Un appel à la méthode awaitsera bloqué jusqu'à ce que le nombre actuel atteigne zéro en raison d'appels de la méthode countDownou de l'expiration du délai imparti. Cette approche permet d'interrompre instantanément un fil 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 le CountDownLatchet le joindu thread vers le 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.");
        } 
    }
}
5

Quelques informations supplémentaires. Le drapeau et l'interruption sont suggérés dans la documentation 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 une entrée), utilisez Thread.interrupt

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

Je n'ai pas eu l'interruption de travailler dans Android, alors 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;
 }
2
Mindborg

En règle générale, un thread est terminé lorsqu'il est interrompu. Alors, pourquoi ne pas utiliser le booléen natif? Essayez 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- Comment puis-je tuer un fil? sans utiliser stop ();

1