web-dev-qa-db-fra.com

Comment temporiser un fil

Je veux exécuter un fil de discussion pour une durée déterminée. 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 découvert à partir de ce thread Consiste à utiliser une TimerTask dans la méthode run () du Thread.

Existe-t-il de meilleures solutions pour cela?


EDIT: Ajouter 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é (du code - je n’ai aucun contrôle sur ce morceau de code)? Si le code est terminé et que sleep () est interrompu, comment peut-il s'agir d'un timeOut?

La tâche à exécuter n'est pas sous mon contrôle. Ce peut être n'importe quel morceau de code. Le problème est que ce morceau de code risque de se heurter à 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 que ce thread se termine et ait besoin de connaître le statut de la tâche (c'est-à-dire si son délai d'expiration a été dépassé ou si une exception s'est produite ou si sa réussite est satisfaisante). Si la tâche passe dans une boucle infinie, mon thread parent continue d'attendre indéfiniment, ce qui n'est pas une situation idéale.

234
java_geek

En effet, utilisez plutôt ExecutorService au lieu 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’argument timeout dans Future#get() method, par ex. augmentez-le à 5 et vous verrez que le fil se termine. Vous pouvez intercepter le délai d'attente dans le bloc catch (TimeoutException e).

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

while (!Thread.interrupted()) {
    // Do your long running task here.
}
345
BalusC

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

Les bibliothèques Java principales telles que ExecutorService annulent les tâches asynchrones avec interrupt() appels sur le thread de travail. Ainsi, par exemple, si la tâche contient une sorte de boucle, vous devriez vérifier son état de l'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 rechercher activement les interruptions; régler une interruption ne fait pas nécessairement n'importe quoi.

Bien sûr, si votre tâche est simple, vous pouvez simplement vérifier l’heure actuelle à chaque itération et abandonner à la fin du délai imparti. Un thread de travail n'est pas nécessaire dans ce cas.

44
erickson

Pensez à utiliser une instance de ExecutorService . Les deux méthodes invokeAll() et invokeAny() sont disponibles avec un paramètre timeout

Le thread en cours sera bloqué jusqu'à la fin de la méthode (ne sachant pas si cela est souhaitable), soit parce que la ou les tâches se sont terminées normalement, soit que le délai a été dépassé. Vous pouvez examiner le Future (s) renvoyé pour déterminer ce qui s'est passé.

13
Drew Wills

BalusC a déclaré:

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

Mais si vous remplacez Thread.sleep(4000); par for (int i = 0; i < 5E8; i++) {}, la compilation ne sera pas compilée, car la boucle vide ne jettera pas de InterruptedException.

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

Cela semble être un problème grave pour moi. Je ne vois pas comment adapter cette réponse à une tâche longue et générale. 

Edité à ajouter: je pose cette question comme une nouvelle question: [ interrompre un fil après un temps fixe, faut-il qu'il lève une exception InterruptedException? ]

8
user1310503

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

A partir de Java documentation mentionnée ci-dessus:

Et 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 à provoquer le retour immédiat du fil. Malheureusement, il y a vraiment Ce n'est pas une technique qui fonctionne en général. Il est à noter que dans toutes les situations où un thread en attente ne répond pas Thread.interrupt, il ne répondrait pas non plus à Thread.stop. Tel les cas incluent des attaques délibérées par déni de service et des opérations d'E/S pour lequel thread.stop et thread.interrupt ne fonctionnent pas correctement.

Résultat final:

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

7
Peter Tseng

J'ai créé une classe d'assistance 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 ça:

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) {}  
5
Peter Goransson

Je pense que vous devriez vous pencher sur les mécanismes de gestion de la concurrence appropriés (les threads qui tournent dans des boucles infinies ne sonnent pas bien en soi, d'ailleurs). Assurez-vous de lire un peu sur le "tuer" ou "arrêter" Threads topic.

Ce que vous décrivez ressemble beaucoup à un "rendez-vous", vous pouvez donc jeter un oeil à/ CyclicBarrier .

Il peut y avoir d’autres constructions (comme utiliser 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 compter le compte à rebours s’il a déjà fonctionné; premier thread soit après un délai d'attente, soit lorsque le compte à rebours du verrouillage est invoqué).

Je recommande généralement deux livres dans ce domaine: Programmation simultanée en Java et Java Concurrency in Practice .

5
Dieter

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 le kill () méthode pour 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

3
elou

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

Le risque que vous suggérez est que vous ne savez probablement pas dans quel état se trouvera le fil lorsque vous le tuez - vous risquez donc d'instabiliser. Une meilleure solution consiste à s’assurer que votre code threadé ne se bloque pas ou répondra bien à une demande d’abandon.

2
Dan Puzey

Grande réponse de BalusC's: 

mais juste pour ajouter que le délai d'attente lui-même n'interrompt pas le fil lui-même. même si vous vérifiez avec while (! Thread.interrupted ()) dans votre tâche. si vous voulez vous assurer que thread est arrêté, vous devez également vous assurer que future.cancel () est appelée lorsque l'exception de délai d'attente 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!!");
      }          
    } 
} 
2
Robocide

Voici ma classe simple à utiliser pour exécuter ou appel morceau de Java code :-)

Ceci est basé sur l'excellent 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;
    }

}
2
Pascal

L'extrait suivant va démarrer une opération dans un thread séparé, puis attendre 10 secondes au maximum pour que l'opération se termine. Si l'opération ne se termine pas à temps, le code tentera de l'annuler, puis continuera sur sa lancée. 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 méthode getExecutorService() peut être implémentée de différentes manières. Si vous n'avez pas d'exigences particulières, vous pouvez simplement appeler Executors.newCachedThreadPool() pour le regroupement de threads sans limite supérieure du nombre de threads.

2
markusk

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 d'attente interrompe 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 le garder 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 préciser vos exigences ou afficher du code.

0
sfussenegger

Maintenant, je rencontre un problème comme celui-ci. Il arrive de décoder une image. Le processus de décodage prend trop de temps pour que l’écran reste noir. J'ajoute un contrôleur de temps: quand le temps est trop long, puis sortez 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);
0
Liu Jing

Je recherchais un ExecutorService capable d'interrompre tous les Runnables expirés exécutés par celui-ci, mais n'en trouvant 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();
}
0
Tom

Dans la solution donnée par BalusC , le thread principal restera bloqué pendant le délai d'attente. Si vous avez un pool de threads avec plusieurs threads, vous aurez besoin du même nombre de threads supplémentaires qui utiliseront Future.get (long délai d'attente, unité TimeUnit) appel bloquant pour attendre et fermer le thread s'il dépasse le délai d'attente. 

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

La classe générique devrait ê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);
        };

    }
}
0
Sumeet Sahu

J'ai eu le même problème. J'ai donc proposé 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ève 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
    }
0