80 votes

Java : définir un délai d'attente sur un certain bloc de code ?

Est-il possible de forcer Java à lancer une exception lorsqu'un bloc de code s'exécute plus longtemps que prévu ?

3voto

laher Points 4882

EDIT : Peter Lawrey a tout à fait raison : ce n'est pas aussi simple que d'interrompre un thread (ma suggestion originale), et les Executors & Callables sont très utiles ...

Plutôt que d'interrompre les threads, vous pourriez définir une variable sur le Callable une fois que le délai d'attente est atteint. L'appelant devrait vérifier cette variable à des moments appropriés de l'exécution de la tâche, pour savoir quand s'arrêter.

Les Callables renvoient des Futures, avec lesquels vous pouvez spécifier un délai d'attente lorsque vous essayez d'"obtenir" le résultat du futur. Quelque chose comme ceci :

try {
   future.get(timeoutSeconds, TimeUnit.SECONDS)
} catch(InterruptedException e) {
   myCallable.setStopMeAtAppropriatePlace(true);
}

Voir Future.get, Executors, et Callable ...

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Future.html#get-long-java.util.concurrent.TimeUnit-

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Callable.html

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool%28int%29

1 votes

Malheureusement, cela ne fonctionne que si vous contrôlez le code qui s'exécute à l'intérieur de l'appelable (si c'est vrai, alors faire cela est trivial).

0 votes

Ok, je peux le voir dans le commentaire de @HTF maintenant. Je suppose que Thread.stop() est le seul moyen ! Voir les avertissements ici : download.oracle.com/javase/6/docs/technotes/guides/concurrency/

2voto

J'ai créé une solution très simple sans utiliser de frameworks ou d'API. Cela semble plus élégant et plus compréhensible. La classe s'appelle TimeoutBlock.

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

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

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

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

Cela m'a été très utile lorsque j'ai dû me connecter à un compte FTP. Parfois, la connexion FTP se bloque ou s'interrompt complètement. Cela provoquait l'arrêt de tout le système. et j'avais besoin d'un moyen de le détecter et de l'empêcher de se produire. J'ai donc créé ce programme et l'ai utilisé. Il fonctionne plutôt bien.

2voto

P3A Points 113

J'ai été confronté à un problème similaire où ma tâche était de pousser un message vers SQS dans un délai particulier. J'ai utilisé la logique triviale de l'exécuter via un autre thread et d'attendre son objet futur en spécifiant le délai. Cela me donnerait une exception TIMEOUT en cas de dépassement de délai.

final Future<ISendMessageResult> future = 
timeoutHelperThreadPool.getExecutor().submit(() -> {
  return getQueueStore().sendMessage(request).get();
});
try {
  sendMessageResult = future.get(200, TimeUnit.MILLISECONDS);
  logger.info("SQS_PUSH_SUCCESSFUL");
  return true;

} catch (final TimeoutException e) {
  logger.error("SQS_PUSH_TIMEOUT_EXCEPTION");
}

Mais il y a des cas où vous ne pouvez pas empêcher le code d'être exécuté par un autre thread et vous obtenez des vrais négatifs dans ce cas.

Par exemple - Dans mon cas, ma demande a atteint SQS et pendant que le message était poussé, ma logique de code a rencontré le timeout spécifié. En réalité, mon message a été poussé dans la file d'attente, mais mon thread principal a supposé qu'il avait échoué en raison de l'exception TIMEOUT. C'est un type de problème qui peut être évité plutôt que résolu. Dans mon cas, je l'ai évité en fournissant un délai d'attente qui suffirait dans presque tous les cas.

Si le code que vous voulez interrompre se trouve à l'intérieur de votre application et n'est pas un appel d'API, alors vous pouvez simplement utiliser

future.cancel(true)

Cependant, n'oubliez pas que la documentation de Java indique qu'elle ne garantit pas que l'exécution sera bloquée.

"Tente d'annuler l'exécution de cette tâche. Cette tentative échoue si la tâche est déjà terminée, si elle a déjà été annulée ou si elle n'a pas pu être annulée pour une autre raison. En cas de succès, et si cette tâche n'a pas encore commencé au moment de l'appel de l'annulation, cette tâche ne devrait jamais s'exécuter. Si la tâche a déjà commencé, le paramètre mayInterruptIfRunning détermine si le fil d'exécution de cette tâche doit être interrompu pour tenter d'arrêter la tâche."

1voto

Reuben Varma Points 21

Au lieu d'avoir la tâche dans le nouveau fil et la minuterie dans le fil principal, ayez la minuterie dans le nouveau fil et la tâche dans le fil principal :

public static class TimeOut implements Runnable{
    public void run() {
        Thread.sleep(10000);
        if(taskComplete ==false) {
            System.out.println("Timed Out");
            return;
        }
        else {
            return;
        }
    }
}
public static boolean taskComplete = false;
public static void main(String[] args) {
    TimeOut timeOut = new TimeOut();
    Thread timeOutThread = new Thread(timeOut);
    timeOutThread.start();
    //task starts here
    //task completed
    taskComplete =true;
    while(true) {//do all other stuff }
}

1 votes

Votre variable TimeOut timeOut dans le main() n'est jamais utilisée.

2 votes

Dans la méthode principale, ce devrait être Thread timeOutThread = new Thread(timeOut); alors @Johannes timeOut serait utilisé.

0 votes

Notez que c'est la seule solution qui soit évolutive. vous n'avez vraiment besoin que d'un seul timer thread, qui pourrait interrompre des milliers de worker threads. dans le cas contraire, vous auriez des milliers de timer threads pour des milliers de worker threads - ce qui n'est pas idéal, c'est le moins qu'on puisse dire.

1voto

Tom Chamberlain Points 2063

Si vous voulez une méthode de type CompletableFuture, vous pourriez avoir une méthode du type

public MyResponseObject retrieveDataFromEndpoint() {

   CompletableFuture<MyResponseObject> endpointCall 
       = CompletableFuture.supplyAsync(() ->
             yourRestService.callEnpoint(withArg1, withArg2));

   try {
       return endpointCall.get(10, TimeUnit.MINUTES);
   } catch (TimeoutException 
               | InterruptedException 
               | ExecutionException e) {
       throw new RuntimeException("Unable to fetch data", e);
   }
}

Si vous utilisez Spring, vous pouvez annoter la méthode avec une balise @Retryable de sorte que la méthode soit réessayée trois fois si une exception est levée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X