Comment expirer un fil

255

Je veux exécuter un thread pendant un certain temps. S'il n'est pas terminé dans ce délai, je veux le tuer, lever une exception ou le gérer d'une manière ou d'une autre. Comment ceci peut être fait?

Une façon de le faire comme je l'ai compris à partir de ce thread est d'utiliser une TimerTask dans la méthode run () du Thread.

Existe-t-il de meilleures solutions pour cela?

 
EDIT: Ajout d'une prime car j'avais besoin d'une réponse plus claire. Le code ExecutorService donné ci-dessous ne résout pas mon problème. Pourquoi devrais-je dormir () après l'exécution (du code - je n'ai aucune poignée sur ce morceau de code)? Si le code est terminé et que sleep () est interrompu, comment cela peut-il être un timeOut?

La tâche qui doit être exécutée n'est pas sous mon contrôle. Il peut s'agir de n'importe quel morceau de code. Le problème est que ce morceau de code peut s'exécuter dans une boucle infinie. Je ne veux pas que cela se produise. Donc, je veux juste exécuter cette tâche dans un thread séparé. Le thread parent doit attendre la fin de ce thread et doit connaître l'état de la tâche (c'est-à-dire s'il a expiré ou si une exception s'est produite ou si c'est un succès). Si la tâche entre dans une boucle infinie, mon thread parent continue d'attendre indéfiniment, ce qui n'est pas une situation idéale.

java_geek
la source
EDIT: Ajout d'une prime car j'avais besoin d'une réponse plus claire. le code ExecutorService donné ci-dessous ne résout pas mon problème. Pourquoi devrais-je dormir () après avoir exécuté mon code? Si le code est terminé et que sleep () est interrompu, comment cela peut-il être un timeOut?
java_geek
7
Ce sleep()n'était qu'un talon pour représenter une «tâche de longue durée». Il suffit de le remplacer par votre vraie tâche;)
BalusC
1
... une "tâche de longue durée" qui arrive à répondre aux interrupt()appels sur son thread ... tous les appels "bloquants" ne le font pas, comme j'ai essayé de le souligner dans ma réponse. Les spécificités de la tâche que vous essayez d'interrompre font une énorme différence dans l'approche à utiliser. Plus d'informations sur la tâche seraient utiles.
erickson
Si ces réponses ne résolvent pas le problème, alors je suppose que plus de détails / code devraient aider à répondre.
Elister
Ces threads que vous souhaitez limiter dans le temps; font-ils des appels bloquants, ou sont-ils dans une boucle où vous pouvez facilement vérifier une variable pour voir s'il est temps de quitter?
Scott Smith

Réponses:

376

En effet plutôt utiliser à la ExecutorServiceplace de Timer, voici un SSCCE :

package com.stackoverflow.q2275443;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class Test {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Task());

        try {
            System.out.println("Started..");
            System.out.println(future.get(3, TimeUnit.SECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            future.cancel(true);
            System.out.println("Terminated!");
        }

        executor.shutdownNow();
    }
}

class Task implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(4000); // Just to demo a long running task of 4 seconds.
        return "Ready!";
    }
}

Jouez un peu avec l' timeoutargument dans la Future#get()méthode, par exemple augmentez-le à 5 et vous verrez que le fil se termine. Vous pouvez intercepter le délai d'expiration dans le catch (TimeoutException e)bloc.

Mise à jour: pour clarifier un malentendu conceptuel, le sleep()n'est pas requis. Il est juste utilisé à des fins de démonstration / SSCCE. Faites simplement votre longue tâche à la place de sleep(). Dans votre longue tâche, vous devriez vérifier si le thread n'est pas interrompu comme suit:

while (!Thread.interrupted()) {
    // Do your long running task here.
}
BalusC
la source
24
Remplacez-la Thread.sleep(4000)par une autre instruction longue et l'exemple ne fonctionnera pas. En d'autres termes, cet exemple ne fonctionnerait que si le Taskest conçu pour comprendre le Thread.isInterrupted()changement de statut.
yegor256
@BalusC J'ai essayé cette approche en essayant de terminer mes discussions, mais je n'ai pas pu le faire fonctionner. Vous pouvez le vérifier ici: stackoverflow.com/questions/35553420/…
syfantid
Comment InterruptedException est-il traité par future.cancel (true)?
bolei
1
n nombre de personnes ont commenté le nom du package, et voici encore un autre +1 pour celui-ci. C'est une si bonne compétence à s'imprégner. Merci!
Ashwin Tumma
@BalusC J'ai un doute, si le futur s'exécutera de manière synchrone et si cela prend plus d'un temps prédéfini alors il serait terminé. Sinon, il s'exécuterait dans le futur dans l'intervalle, nous comptons sur le temps ... Merci
Adeel Ahmad
49

Il n'y a pas de méthode fiable à 100% pour effectuer une tâche ancienne. La tâche doit être écrite avec cette capacité à l'esprit.

Les bibliothèques Java de base aiment ExecutorServiceannuler les tâches asynchrones avec des interrupt()appels sur le thread de travail. Ainsi, par exemple, si la tâche contient une sorte de boucle, vous devez vérifier son état d'interruption à chaque itération. Si la tâche effectue des opérations d'E / S, elles doivent également être interruptibles et leur configuration peut être délicate. Dans tous les cas, gardez à l'esprit que le code doit vérifier activement les interruptions; définir une interruption ne fait rien nécessairement.

Bien sûr, si votre tâche est une simple boucle, vous pouvez simplement vérifier l'heure actuelle à chaque itération et abandonner lorsqu'un délai spécifié s'est écoulé. Un thread de travail n'est pas nécessaire dans ce cas.

erickson
la source
D'après mon expérience, le seul code ne réagissant pas pour commencer interrompu est le blocage en code natif (en attente du système d'exploitation).
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen Je suis d'accord, mais c'est beaucoup de code. Ce que je veux dire, c'est qu'il n'y a pas de mécanisme à usage général pour cela; vous devez comprendre la politique d'interruption de la tâche.
erickson
@erickson, je suis d'accord avec vous. Pour répondre à la question, Il doit y avoir une politique d'annulation définie pour chaque tâche, si vous êtes intéressé à l'arrêter de cette façon. Ou le thread doit être conscient de ce qu'il est censé faire lorsqu'il est interrompu. Après tout, l'interruption et l'arrêt de tout thread n'est qu'une demande que le thread cible peut accepter ou rejeter, il est donc préférable d'écrire la tâche en gardant cela à l'esprit.
AKS
cant executorservice choisir d'exécuter la tâche sur le thread appelant? le service d'exécution peut également choisir d'exécuter la tâche dans le futur?
filthy_wizard
@ user1232726 La execute()méthode de l'interface parent Executorpeut exécuter une tâche dans le thread appelant. Il n'y a pas de déclaration similaire pour les submit()méthodes de ExecutorServiceces Futureinstances de retour . L'implication du service est qu'il existe des threads de travail qui doivent être nettoyés via l'arrêt et que les tâches sont exécutées de manière asynchrone. Cela dit, rien dans le contrat ne dit qu'il ExecutorServiceest interdit d'exécuter des tâches dans le thread de soumission; ces garanties proviennent des API d'implémentation, comme les Executorsusines.
erickson
13

Pensez à utiliser une instance d' ExecutorService . Les deux méthodes invokeAll()et invokeAny()sont disponibles avec un timeoutparamètre.

Le thread actuel se bloquera jusqu'à ce que la méthode soit terminée (je ne sais pas si cela est souhaitable) soit parce que la ou les tâches se sont terminées normalement ou que le délai a été atteint. Vous pouvez inspecter le Future(s) retour (s) pour déterminer ce qui s'est passé.

Drew Wills
la source
9

En supposant que le code de thread est hors de votre contrôle:

De la documentation Java mentionnée ci-dessus:

Que faire si un thread ne répond pas à Thread.interrupt?

Dans certains cas, vous pouvez utiliser des astuces spécifiques à l'application. Par exemple, si un thread attend sur un socket connu, vous pouvez fermer le socket pour provoquer le retour immédiat du thread. Malheureusement, il n'y a vraiment aucune technique qui fonctionne en général. Il convient de noter que dans toutes les situations où un thread en attente ne répond pas à Thread.interrupt, il ne répondra pas non plus à Thread.stop. De tels cas incluent des attaques délibérées par déni de service et des opérations d'E / S pour lesquelles thread.stop et thread.interrupt ne fonctionnent pas correctement.

Conclusion:

Assurez-vous que tous les threads peuvent être interrompus, sinon vous avez besoin d'une connaissance spécifique du thread - comme avoir un indicateur à définir. Peut-être pouvez-vous exiger que la tâche vous soit donnée avec le code nécessaire pour l'arrêter - définissez une interface avec une stop()méthode. Vous pouvez également avertir lorsque vous n'avez pas réussi à arrêter une tâche.

Peter Tseng
la source
8

BalusC a déclaré:

Mise à jour: pour clarifier un malentendu conceptuel, le sleep () n'est pas requis. Il est juste utilisé à des fins de SSCCE / démonstration. Faites simplement votre longue tâche à la place du sommeil ().

Mais si vous remplacez Thread.sleep(4000);par for (int i = 0; i < 5E8; i++) {}alors il ne se compile pas, car la boucle vide ne lance pas un InterruptedException.

Et pour que le thread soit interruptible, il doit lancer un InterruptedException.

Cela me semble être un problème grave. Je ne vois pas comment adapter cette réponse pour travailler avec une tâche générale de longue durée.

Modifié pour ajouter: j'ai reformulé ceci comme une nouvelle question: [ interrompre un thread après un temps fixe, doit-il lever InterruptedException? ]

user1310503
la source
La façon dont je le fais est d'ajouter une «exception de throws» dans la méthode d'appel public de classe <T> {}
Roberto Linares
5

Je pense que vous devriez jeter un coup d'œil aux mécanismes de gestion des accès concurrents (les threads s'exécutant dans des boucles infinies ne sonnent pas bien en soi, btw). Assurez-vous de lire un peu sur le sujet "Tuer" ou "arrêter" les discussions .

Ce que vous décrivez ressemble beaucoup à un "rendez-vous", donc vous voudrez peut-être jeter un œil au CyclicBarrier .

Il peut y avoir d'autres constructions (comme l'utilisation de CountDownLatch par exemple) qui peuvent résoudre votre problème (un thread attend avec un délai d'attente pour le verrou, l'autre doit décompter le verrou s'il a fait son travail, ce qui libérerait votre premier thread soit après une temporisation ou lorsque le compte à rebours du verrou est invoqué).

Je recommande généralement deux livres dans ce domaine: la programmation simultanée en Java et la concurrence Java en pratique .

Dieter
la source
5

J'ai créé une classe d'aide juste pour cela il y a quelque temps. Fonctionne très bien:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
 * TimeOut class - used for stopping a thread that is taking too long
 * @author Peter Goransson
 *
 */
public class TimeOut {

    Thread interrupter;
    Thread target;
    long timeout;
    boolean success;
    boolean forceStop;

    CyclicBarrier barrier;

    /**
     * 
     * @param target The Runnable target to be executed
     * @param timeout The time in milliseconds before target will be interrupted or stopped
     * @param forceStop If true, will Thread.stop() this target instead of just interrupt() 
     */
    public TimeOut(Runnable target, long timeout, boolean forceStop) {      
        this.timeout = timeout;
        this.forceStop = forceStop;

        this.target = new Thread(target);       
        this.interrupter = new Thread(new Interrupter());

        barrier = new CyclicBarrier(2); // There will always be just 2 threads waiting on this barrier
    }

    public boolean execute() throws InterruptedException {  

        // Start target and interrupter
        target.start();
        interrupter.start();

        // Wait for target to finish or be interrupted by interrupter
        target.join();  

        interrupter.interrupt(); // stop the interrupter    
        try {
            barrier.await(); // Need to wait on this barrier to make sure status is set
        } catch (BrokenBarrierException e) {
            // Something horrible happened, assume we failed
            success = false;
        } 

        return success; // status is set in the Interrupter inner class
    }

    private class Interrupter implements Runnable {

        Interrupter() {}

        public void run() {
            try {
                Thread.sleep(timeout); // Wait for timeout period and then kill this target
                if (forceStop) {
                  target.stop(); // Need to use stop instead of interrupt since we're trying to kill this thread
                }
                else {
                    target.interrupt(); // Gracefully interrupt the waiting thread
                }
                System.out.println("done");             
                success = false;
            } catch (InterruptedException e) {
                success = true;
            }


            try {
                barrier.await(); // Need to wait on this barrier
            } catch (InterruptedException e) {
                // If the Child and Interrupter finish at the exact same millisecond we'll get here
                // In this weird case assume it failed
                success = false;                
            } 
            catch (BrokenBarrierException e) {
                // Something horrible happened, assume we failed
                success = false;
            }

        }

    }
}

Cela s'appelle comme ceci:

long timeout = 10000; // number of milliseconds before timeout
TimeOut t = new TimeOut(new PhotoProcessor(filePath, params), timeout, true);
try {                       
  boolean sucess = t.execute(); // Will return false if this times out
  if (!sucess) {
    // This thread timed out
  }
  else {
    // This thread ran completely and did not timeout
  }
} catch (InterruptedException e) {}  
Peter Goransson
la source
3

Je vous poste un morceau de code qui montre comment résoudre le problème. Par exemple, je lis un fichier. Vous pouvez utiliser cette méthode pour une autre opération, mais vous devez implémenter la méthode kill () afin que l'opération principale soit interrompue.

J'espère que ça aide


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Main class
 * 
 * @author el
 * 
 */
public class Main {
    /**
     * Thread which perform the task which should be timed out.
     * 
     * @author el
     * 
     */
    public static class MainThread extends Thread {
        /**
         * For example reading a file. File to read.
         */
        final private File fileToRead;
        /**
         * InputStream from the file.
         */
        final private InputStream myInputStream;
        /**
         * Thread for timeout.
         */
        final private TimeOutThread timeOutThread;

        /**
         * true if the thread has not ended.
         */
        boolean isRunning = true;

        /**
         * true if all tasks where done.
         */
        boolean everythingDone = false;

        /**
         * if every thing could not be done, an {@link Exception} may have
         * Happens.
         */
        Throwable endedWithException = null;

        /**
         * Constructor.
         * 
         * @param file
         * @throws FileNotFoundException
         */
        MainThread(File file) throws FileNotFoundException {
            setDaemon(false);
            fileToRead = file;
            // open the file stream.
            myInputStream = new FileInputStream(fileToRead);
            // Instantiate the timeout thread.
            timeOutThread = new TimeOutThread(10000, this);
        }

        /**
         * Used by the {@link TimeOutThread}.
         */
        public void kill() {
            if (isRunning) {
                isRunning = false;
                if (myInputStream != null) {
                    try {
                        // close the stream, it may be the problem.
                        myInputStream.close();
                    } catch (IOException e) {
                        // Not interesting
                        System.out.println(e.toString());
                    }
                }
                synchronized (this) {
                    notify();
                }
            }
        }

        /**
         * The task which should be timed out.
         */
        @Override
        public void run() {
            timeOutThread.start();
            int bytes = 0;
            try {
                // do something
                while (myInputStream.read() >= 0) {
                    // may block the thread.
                    myInputStream.read();
                    bytes++;
                    // simulate a slow stream.
                    synchronized (this) {
                        wait(10);
                    }
                }
                everythingDone = true;
            } catch (IOException e) {
                endedWithException = e;
            } catch (InterruptedException e) {
                endedWithException = e;
            } finally {
                timeOutThread.kill();
                System.out.println("-->read " + bytes + " bytes.");
                isRunning = false;
                synchronized (this) {
                    notifyAll();
                }
            }
        }
    }

    /**
     * Timeout Thread. Kill the main task if necessary.
     * 
     * @author el
     * 
     */
    public static class TimeOutThread extends Thread {
        final long timeout;
        final MainThread controlledObj;

        TimeOutThread(long timeout, MainThread controlledObj) {
            setDaemon(true);
            this.timeout = timeout;
            this.controlledObj = controlledObj;
        }

        boolean isRunning = true;

        /**
         * If we done need the {@link TimeOutThread} thread, we may kill it.
         */
        public void kill() {
            isRunning = false;
            synchronized (this) {
                notify();
            }
        }

        /**
         * 
         */
        @Override
        public void run() {
            long deltaT = 0l;
            try {
                long start = System.currentTimeMillis();
                while (isRunning && deltaT < timeout) {
                    synchronized (this) {
                        wait(Math.max(100, timeout - deltaT));
                    }
                    deltaT = System.currentTimeMillis() - start;
                }
            } catch (InterruptedException e) {
                // If the thread is interrupted,
                // you may not want to kill the main thread,
                // but probably yes.
            } finally {
                isRunning = false;
            }
            controlledObj.kill();
        }
    }

    /**
     * Start the main task and wait for the end.
     * 
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String[] args) throws FileNotFoundException {
        long start = System.currentTimeMillis();
        MainThread main = new MainThread(new File(args[0]));
        main.start();
        try {
            while (main.isRunning) {
                synchronized (main) {
                    main.wait(1000);
                }
            }
            long stop = System.currentTimeMillis();

            if (main.everythingDone)
                System.out.println("all done in " + (stop - start) + " ms.");
            else {
                System.out.println("could not do everything in "
                        + (stop - start) + " ms.");
                if (main.endedWithException != null)
                    main.endedWithException.printStackTrace();
            }
        } catch (InterruptedException e) {
            System.out.println("You've killed me!");
        }
    }
}

Cordialement

elou
la source
3

Voici ma classe d'assistance vraiment simple à utiliser pour exécuter ou appeler un morceau de code Java :-)

Ceci est basé sur l'excellente réponse de BalusC

package com.mycompany.util.concurrent;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * Calling {@link Callable#call()} or Running {@link Runnable#run()} code
 * with a timeout based on {@link Future#get(long, TimeUnit))}
 * @author pascaldalfarra
 *
 */
public class CallableHelper
{

    private CallableHelper()
    {
    }

    public static final void run(final Runnable runnable, int timeoutInSeconds)
    {
        run(runnable, null, timeoutInSeconds);
    }

    public static final void run(final Runnable runnable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        call(new Callable<Void>()
        {
            @Override
            public Void call() throws Exception
            {
                runnable.run();
                return null;
            }
        }, timeoutCallback, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, int timeoutInSeconds)
    {
        return call(callable, null, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        try
        {
            Future<T> future = executor.submit(callable);
            T result = future.get(timeoutInSeconds, TimeUnit.SECONDS);
            System.out.println("CallableHelper - Finished!");
            return result;
        }
        catch (TimeoutException e)
        {
            System.out.println("CallableHelper - TimeoutException!");
            if(timeoutCallback != null)
            {
                timeoutCallback.run();
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        catch (ExecutionException e)
        {
            e.printStackTrace();
        }
        finally
        {
            executor.shutdownNow();
            executor = null;
        }

        return null;
    }

}
Pascal
la source
2

L'extrait de code suivant démarrera une opération dans un thread séparé, puis attendra jusqu'à 10 secondes pour que l'opération se termine. Si l'opération ne se termine pas à temps, le code tentera d'annuler l'opération, puis poursuivra son chemin joyeux. Même si l'opération ne peut pas être annulée facilement, le thread parent n'attendra pas la fin du thread enfant.

ExecutorService executorService = getExecutorService();
Future<SomeClass> future = executorService.submit(new Callable<SomeClass>() {
    public SomeClass call() {
        // Perform long-running task, return result. The code should check
        // interrupt status regularly, to facilitate cancellation.
    }
});
try {
    // Real life code should define the timeout as a constant or
    // retrieve it from configuration
    SomeClass result = future.get(10, TimeUnit.SECONDS);
    // Do something with the result
} catch (TimeoutException e) {
    future.cancel(true);
    // Perform other error handling, e.g. logging, throwing an exception
}

La getExecutorService()méthode peut être mise en œuvre de plusieurs façons. Si vous n'avez pas d'exigences particulières, vous pouvez simplement demander Executors.newCachedThreadPool()un pool de threads sans limite supérieure sur le nombre de threads.

Markusk
la source
Quelles sont les importations requises? Quels sont SomeClasset Future?
ADTC
2

Une chose que je n'ai pas vue mentionnée est que tuer les threads est généralement une mauvaise idée. Il existe des techniques pour rendre les méthodes threadées proprement abandonnées , mais c'est différent de simplement tuer un thread après un délai d'expiration.

Le risque avec ce que vous proposez est que vous ne savez probablement pas dans quel état sera le thread lorsque vous le tuerez - vous risquez donc d'introduire de l'instabilité. Une meilleure solution consiste à vous assurer que votre code threadé ne se bloque pas lui-même ou qu'il répond bien à une demande d'abandon.

Dan Puzey
la source
Sans contexte, une déclaration comme la vôtre semble trop restrictive. En milieu universitaire, j'ai très souvent besoin de tester quelque chose jusqu'à un délai d'attente, et quand cela se produit, je laisse simplement tomber tout le calcul et enregistre que le délai d'attente s'est produit. C'est probablement rare dans l'industrie, mais quand même ...
Alessandro S.
@AlessandroS: c'est un point raisonnable, bien que l'OP ait demandé de "meilleures solutions", par lesquelles j'ai compris que la robustesse et la fiabilité étaient préférées à la force brute.
Dan Puzey
2

Excellente réponse de BalusC:

mais juste pour ajouter que le timeout lui-même n'interrompt pas le thread lui-même. même si vous vérifiez avec ((Thread.interrupted ()) dans votre tâche. si vous voulez vous assurer que le thread est arrêté, vous devez également vous assurer que future.cancel () est invoqué lorsque l'exception d'expiration est interceptée.

package com.stackoverflow.q2275443; 

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;


public class Test { 
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<String> future = executor.submit(new Task());

        try { 
            System.out.println("Started..");
            System.out.println(future.get(3, TimeUnit.SECONDS));
            System.out.println("Finished!");
        } catch (TimeoutException e) {
            //Without the below cancel the thread will continue to live 
            // even though the timeout exception thrown.
            future.cancel();
            System.out.println("Terminated!");
        } 

        executor.shutdownNow();
    } 
} 

class Task implements Callable<String> {
    @Override 
    public String call() throws Exception {
      while(!Thread.currentThread.isInterrupted()){
          System.out.println("Im still running baby!!");
      }          
    } 
} 
Robocide
la source
0

Je pense que la réponse dépend principalement de la tâche elle-même.

  • Est-ce qu'il fait une tâche encore et encore?
  • Est-il nécessaire que le délai interrompt une tâche en cours d'exécution immédiatement après son expiration?

Si la première réponse est oui et la seconde est non, vous pouvez rester aussi simple que cela:

public class Main {

    private static final class TimeoutTask extends Thread {
        private final long _timeoutMs;
        private Runnable _runnable;

        private TimeoutTask(long timeoutMs, Runnable runnable) {
            _timeoutMs = timeoutMs;
            _runnable = runnable;
        }

        @Override
        public void run() {
            long start = System.currentTimeMillis();
            while (System.currentTimeMillis() < (start + _timeoutMs)) {
                _runnable.run();
            }
            System.out.println("execution took " + (System.currentTimeMillis() - start) +" ms");
        }

    }

    public static void main(String[] args) throws Exception {
        new TimeoutTask(2000L, new Runnable() {

            @Override
            public void run() {
                System.out.println("doing something ...");
                try {
                    // pretend it's taking somewhat longer than it really does
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }
}

Si ce n'est pas une option, veuillez restreindre vos exigences - ou montrer du code.

sfussenegger
la source
0

Je cherchais un ExecutorService qui peut interrompre tous les Runnables expirés exécutés par lui, mais n'en ai trouvé aucun. Après quelques heures, j'en ai créé un comme ci-dessous. Cette classe peut être modifiée pour améliorer la robustesse.

public class TimedExecutorService extends ThreadPoolExecutor {
    long timeout;
    public TimedExecutorService(int numThreads, long timeout, TimeUnit unit) {
        super(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(numThreads + 1));
        this.timeout = unit.toMillis(timeout);
    }

    @Override
    protected void beforeExecute(Thread thread, Runnable runnable) {
        Thread interruptionThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // Wait until timeout and interrupt this thread
                    Thread.sleep(timeout);
                    System.out.println("The runnable times out.");
                    thread.interrupt();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        interruptionThread.start();
    }
}

Usage:

public static void main(String[] args) {

    Runnable abcdRunnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("abcdRunnable started");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                // logger.info("The runnable times out.");
            }
            System.out.println("abcdRunnable ended");
        }
    };

    Runnable xyzwRunnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("xyzwRunnable started");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                // logger.info("The runnable times out.");
            }
            System.out.println("xyzwRunnable ended");
        }
    };

    int numThreads = 2, timeout = 5;
    ExecutorService timedExecutor = new TimedExecutorService(numThreads, timeout, TimeUnit.SECONDS);
    timedExecutor.execute(abcdRunnable);
    timedExecutor.execute(xyzwRunnable);
    timedExecutor.shutdown();
}
À M
la source
0

Maintenant, je rencontre un problème comme celui-ci. Il arrive de décoder l'image. Le processus de décodage prend trop de temps pour que l'écran reste noir. l ajouter un contrôleur de temps: lorsque le temps est trop long, puis surgissez du fil actuel. Ce qui suit est le diff:

   ExecutorService executor = Executors.newSingleThreadExecutor();
   Future<Bitmap> future = executor.submit(new Callable<Bitmap>() {
       @Override
       public Bitmap call() throws Exception {
       Bitmap bitmap = decodeAndScaleBitmapFromStream(context, inputUri);// do some time consuming operation
       return null;
            }
       });
       try {
           Bitmap result = future.get(1, TimeUnit.SECONDS);
       } catch (TimeoutException e){
           future.cancel(true);
       }
       executor.shutdown();
       return (bitmap!= null);
Liu Jing
la source
0

J'ai eu le même problème. J'ai donc trouvé une solution simple comme celle-ci.

public class TimeoutBlock {

 private final long timeoutMilliSeconds;
    private long timeoutInteval=100;

    public TimeoutBlock(long timeoutMilliSeconds){
        this.timeoutMilliSeconds=timeoutMilliSeconds;
    }

    public void addBlock(Runnable runnable) throws Throwable{
        long collectIntervals=0;
        Thread timeoutWorker=new Thread(runnable);
        timeoutWorker.start();
        do{ 
            if(collectIntervals>=this.timeoutMilliSeconds){
                timeoutWorker.stop();
                throw new Exception("<<<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated.");
            }
            collectIntervals+=timeoutInteval;           
            Thread.sleep(timeoutInteval);

        }while(timeoutWorker.isAlive());
        System.out.println("<<<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds.");
    }

    /**
     * @return the timeoutInteval
     */
    public long getTimeoutInteval() {
        return timeoutInteval;
    }

    /**
     * @param timeoutInteval the timeoutInteval to set
     */
    public void setTimeoutInteval(long timeoutInteval) {
        this.timeoutInteval = timeoutInteval;
    }
}

Garantit que si le bloc ne s'est pas exécuté dans le délai imparti. le processus se terminera et lèvera une exception.

exemple :

try {
        TimeoutBlock timeoutBlock = new TimeoutBlock(10 * 60 * 1000);//set timeout in milliseconds
        Runnable block=new Runnable() {

            @Override
            public void run() {
                //TO DO write block of code 
            }
        };

        timeoutBlock.addBlock(block);// execute the runnable block 

    } catch (Throwable e) {
        //catch the exception here . Which is block didn't execute within the time limit
    }
Niroshan Abeywickrama
la source
0

Dans la solution proposée par BalusC , le thread principal restera bloqué pendant la période de temporisation. Si vous disposez d'un pool de threads avec plusieurs threads, vous aurez besoin du même nombre de threads supplémentaires qui utiliseront Future.get (long timeout, TimeUnit unit) blocking call pour attendre et fermer le thread s'il dépasse le délai d'expiration.

Une solution générique à ce problème consiste à créer un décorateur ThreadPoolExecutor qui peut ajouter la fonctionnalité de délai d'expiration. Cette classe Decorator doit créer autant de threads que ThreadPoolExecutor, et tous ces threads doivent être utilisés uniquement pour attendre et fermer ThreadPoolExecutor.

La classe générique doit être implémentée comme ci-dessous:

import java.util.List;
import java.util.concurrent.*;

public class TimeoutThreadPoolDecorator extends ThreadPoolExecutor {


    private final ThreadPoolExecutor commandThreadpool;
    private final long timeout;
    private final TimeUnit unit;

    public TimeoutThreadPoolDecorator(ThreadPoolExecutor threadpool,
                                      long timeout,
                                      TimeUnit unit ){
        super(  threadpool.getCorePoolSize(),
                threadpool.getMaximumPoolSize(),
                threadpool.getKeepAliveTime(TimeUnit.MILLISECONDS),
                TimeUnit.MILLISECONDS,
                threadpool.getQueue());

        this.commandThreadpool = threadpool;
        this.timeout=timeout;
        this.unit=unit;
    }

    @Override
    public void execute(Runnable command) {
        super.execute(() -> {
            Future<?> future = commandThreadpool.submit(command);
            try {
                future.get(timeout, unit);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (ExecutionException | TimeoutException e) {
                throw new RejectedExecutionException(e);
            } finally {
                future.cancel(true);
            }
        });
    }

    @Override
    public void setCorePoolSize(int corePoolSize) {
        super.setCorePoolSize(corePoolSize);
        commandThreadpool.setCorePoolSize(corePoolSize);
    }

    @Override
    public void setThreadFactory(ThreadFactory threadFactory) {
        super.setThreadFactory(threadFactory);
        commandThreadpool.setThreadFactory(threadFactory);
    }

    @Override
    public void setMaximumPoolSize(int maximumPoolSize) {
        super.setMaximumPoolSize(maximumPoolSize);
        commandThreadpool.setMaximumPoolSize(maximumPoolSize);
    }

    @Override
    public void setKeepAliveTime(long time, TimeUnit unit) {
        super.setKeepAliveTime(time, unit);
        commandThreadpool.setKeepAliveTime(time, unit);
    }

    @Override
    public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
        super.setRejectedExecutionHandler(handler);
        commandThreadpool.setRejectedExecutionHandler(handler);
    }

    @Override
    public List<Runnable> shutdownNow() {
        List<Runnable> taskList = super.shutdownNow();
        taskList.addAll(commandThreadpool.shutdownNow());
        return taskList;
    }

    @Override
    public void shutdown() {
        super.shutdown();
        commandThreadpool.shutdown();
    }
}

Le décorateur ci-dessus peut être utilisé comme ci-dessous:

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args){

        long timeout = 2000;

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 10, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<>(true));

        threadPool = new TimeoutThreadPoolDecorator( threadPool ,
                timeout,
                TimeUnit.MILLISECONDS);


        threadPool.execute(command(1000));
        threadPool.execute(command(1500));
        threadPool.execute(command(2100));
        threadPool.execute(command(2001));

        while(threadPool.getActiveCount()>0);
        threadPool.shutdown();


    }

    private static Runnable command(int i) {

        return () -> {
            System.out.println("Running Thread:"+Thread.currentThread().getName());
            System.out.println("Starting command with sleep:"+i);
            try {
                Thread.sleep(i);
            } catch (InterruptedException e) {
                System.out.println("Thread "+Thread.currentThread().getName()+" with sleep of "+i+" is Interrupted!!!");
                return;
            }
            System.out.println("Completing Thread "+Thread.currentThread().getName()+" after sleep of "+i);
        };

    }
}
Sumeet Sahu
la source