Cómo expirar un hilo

255

Quiero ejecutar un hilo por un tiempo fijo. Si no se completa dentro de ese tiempo, quiero matarlo, lanzar alguna excepción o manejarlo de alguna manera. ¿Cómo puede hacerse esto?

Una forma de hacerlo como descubrí de este hilo es usar un TimerTask dentro del método run () del hilo.

¿Hay alguna solución mejor para esto?

 
EDITAR: Agregar una recompensa ya que necesitaba una respuesta más clara. El código de ExecutorService que se proporciona a continuación no soluciona mi problema. ¿Por qué debería dormir () después de ejecutar (algún código, no tengo control sobre este fragmento de código)? Si se completa el código y se interrumpe el sueño (), ¿cómo puede ser un tiempo de espera?

La tarea que debe ejecutarse no está bajo mi control. Puede ser cualquier pieza de código. El problema es que este fragmento de código podría encontrarse en un bucle infinito. No quiero que eso suceda. Entonces, solo quiero ejecutar esa tarea en un hilo separado. El subproceso principal tiene que esperar hasta que finalice el subproceso y necesita saber el estado de la tarea (es decir, si se agotó el tiempo de espera o si se produjo alguna excepción o si es un éxito). Si la tarea entra en un bucle infinito, mi hilo padre sigue esperando indefinidamente, lo cual no es una situación ideal.

java_geek
fuente
EDITAR: Agregar una recompensa ya que necesitaba una respuesta más clara. El código de ExecutorService que se proporciona a continuación no soluciona mi problema. ¿Por qué debería dormir () después de ejecutar mi código? Si se completa el código y se interrumpe el sueño (), ¿cómo puede ser un tiempo de espera?
java_geek
77
Eso sleep()fue solo un trozo para representar la "tarea de ejecución prolongada". Simplemente reemplácelo con su tarea real;)
BalusC
1
... una "tarea de ejecución prolongada" que responde a las interrupt()llamadas en su hilo ... no todas las llamadas de "bloqueo" lo hacen, como intenté señalar en mi respuesta. Los detalles de la tarea que está intentando abortar marcan una gran diferencia en el enfoque que debe usarse. Sería útil más información sobre la tarea.
erickson
Si estas respuestas no resuelven el problema, entonces supongo que más detalles / código deberían ayudar a responder.
Elister
Estos hilos que desea limitar de tiempo; ¿están haciendo llamadas de bloqueo o están en algún bucle en el que podría verificar fácilmente alguna variable para ver si es hora de abandonar?
Scott Smith

Respuestas:

376

De hecho en lugar usar ExecutorServiceen lugar de Timer, aquí hay 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!";
    }
}

Juegue un poco con el timeoutargumento en el Future#get()método, por ejemplo, aumente a 5 y verá que el hilo termina. Puede interceptar el tiempo de espera en el catch (TimeoutException e)bloque.

Actualización: para aclarar un malentendido conceptual, el sleep()es no necesaria. Solo se utiliza con fines de demostración / SSCCE. Simplemente haga su tarea de larga duración allí mismo en lugar de sleep(). Dentro de su tarea de larga ejecución, debe verificar si el hilo no se interrumpe de la siguiente manera:

while (!Thread.interrupted()) {
    // Do your long running task here.
}
BalusC
fuente
24
Reemplace Thread.sleep(4000)con alguna otra declaración de larga duración y el ejemplo no funcionará. En otras palabras, este ejemplo funcionaría solo si Taskestá diseñado para comprender el Thread.isInterrupted()cambio de estado.
yegor256
@BalusC Intenté este enfoque tratando de terminar mis hilos, pero no pude hacerlo funcionar. Puede consultarlo aquí: stackoverflow.com/questions/35553420/…
syfantid
¿Cómo se maneja InterruptedException por future.cancel (true)?
bolei
1
Un número de personas ha comentado sobre el nombre del paquete, y aquí hay otro +1 para él. Esa es una habilidad tan buena para ser absorbida. ¡Gracias!
Ashwin Tumma
@BalusC Tengo una duda, si el Futuro se ejecutará sincrónicamente y si lleva más de un tiempo predefinido, entonces se terminaría. De lo contrario, se ejecutaría en el futuro en algún momento mientras contamos con el tiempo ... Gracias
Adeel Ahmad
49

No hay una forma 100% confiable de hacer esto para cualquier tarea antigua. La tarea tiene que ser escrita con esta habilidad en mente.

Las bibliotecas principales de Java, como ExecutorServicecancelar tareas asincrónicas con interrupt()llamadas en el subproceso de trabajo. Entonces, por ejemplo, si la tarea contiene algún tipo de ciclo, debería verificar su estado de interrupción en cada iteración. Si la tarea está haciendo operaciones de E / S, también deberían ser interrumpibles, y configurarlo puede ser complicado. En cualquier caso, tenga en cuenta que el código debe verificar activamente las interrupciones; establecer una interrupción no necesariamente hace nada.

Por supuesto, si su tarea es un ciclo simple, puede verificar el tiempo actual en cada iteración y darse por vencido cuando haya transcurrido un tiempo de espera especificado. Un hilo de trabajo no es necesario en ese caso.

erickson
fuente
En mi experiencia, el único código que no reacciona para comenzar a interrumpirse es el bloqueo en el código nativo (esperando el sistema operativo).
Thorbjørn Ravn Andersen
@ ThorbjørnRavnAndersen Estoy de acuerdo, pero eso es mucho código. Mi punto es que no hay un mecanismo de propósito general para esto; Debe comprender la política de interrupción de la tarea.
erickson
@erickson, estoy de acuerdo con tu. Con respecto al punto de respuesta, debe haber una política de cancelación definida para cada tarea, si está interesado en detenerla de esa manera. O el hilo debe ser consciente de lo que se supone que debe hacer cuando se interrumpe. Después de todo, interrumpir y detener cualquier subproceso es solo una solicitud que el subproceso de destino podría aceptar o rechazar, por lo que es mejor escribir la tarea teniendo esto en cuenta.
AKS
¿no puede el servicio ejecutor optar por ejecutar la tarea en el hilo de llamada? ¿también el servicio ejecutor puede optar por ejecutar la tarea en algún momento en el futuro?
filthy_wizard
@ user1232726 El execute()método de la interfaz principal Executorpuede ejecutar una tarea en el subproceso de llamada. No hay una declaración similar para los submit()métodos de ExecutorServiceesas Futureinstancias de retorno . La implicación del servicio es que hay subprocesos de trabajo que deben limpiarse mediante apagado, y que las tareas se ejecutan de forma asincrónica. Dicho esto, no hay nada en el contrato que diga que ExecutorServiceestá prohibido ejecutar tareas en el hilo de envío; esas garantías provienen de las API de implementación, como las Executorsfábricas.
erickson
13

Considere usar una instancia de ExecutorService . Ambos invokeAll()y los invokeAny()métodos están disponibles con un timeoutparámetro.

El subproceso actual se bloqueará hasta que se complete el método (no estoy seguro si esto es deseable) ya sea porque las tareas se completaron normalmente o se alcanzó el tiempo de espera. Puede inspeccionar las devoluciones Futurepara determinar qué sucedió.

Drew Wills
fuente
9

Asumiendo que el código del hilo está fuera de su control:

De la documentación de Java mencionada anteriormente:

¿Qué pasa si un hilo no responde a Thread.interrupt?

En algunos casos, puede usar trucos específicos de la aplicación. Por ejemplo, si un subproceso está esperando en un socket conocido, puede cerrar el socket para que el subproceso vuelva inmediatamente. Desafortunadamente, realmente no hay ninguna técnica que funcione en general. Cabe señalar que en todas las situaciones en las que un hilo en espera no responde a Thread.interrupt, tampoco respondería a Thread.stop. Dichos casos incluyen ataques deliberados de denegación de servicio y operaciones de E / S para las que thread.stop e thread.interrupt no funcionan correctamente.

Línea de fondo:

Asegúrese de que todos los hilos se puedan interrumpir, o de lo contrario necesita un conocimiento específico del hilo, como tener una bandera para establecer. Tal vez pueda requerir que se le dé la tarea junto con el código necesario para detenerla: defina una interfaz con un stop()método. También puede advertir cuando no pudo detener una tarea.

Peter Tseng
fuente
8

BalusC dijo:

Actualización: para aclarar un malentendido conceptual, no se requiere el sleep (). Solo se usa con fines de demostración / SSCCE. Simplemente haga su tarea de larga duración allí en lugar de dormir ().

Pero si reemplaza Thread.sleep(4000);con, for (int i = 0; i < 5E8; i++) {}entonces no se compila, porque el bucle vacío no arroja un InterruptedException.

Y para que el hilo sea interrumpible, necesita lanzar un InterruptedException.

Esto me parece un problema grave. No puedo ver cómo adaptar esta respuesta para trabajar con una tarea general de larga duración.

Editado para agregar: lo volví a plantear como una nueva pregunta: [ interrumpiendo un hilo después de un tiempo fijo, ¿tiene que arrojar InterruptedException? ]

usuario1310503
fuente
La forma en que lo hago es agregar una 'Excepción de lanzamiento' en el método público Clase <T> call {}
Roberto Linares
5

Creo que debería echar un vistazo a los mecanismos adecuados de manejo de concurrencia (los hilos que se ejecutan en bucles infinitos no suena bien per se, por cierto). Asegúrese de leer un poco sobre el tema "matar" o "detener" temas .

Lo que está describiendo, suena muy parecido a una "cita", por lo que es posible que desee echar un vistazo a la CyclicBarrier .

Puede haber otras construcciones (como usar CountDownLatch, por ejemplo) que pueden resolver su problema (un hilo esperando con un tiempo de espera para el pestillo, el otro debería contar el pestillo si ha hecho su trabajo, lo que liberaría su primer hilo después un tiempo de espera o cuando se invoca la cuenta regresiva del pestillo).

Por lo general, recomiendo dos libros en esta área: programación concurrente en Java y concurrencia de Java en la práctica .

Dieter
fuente
5

Creé una clase auxiliar solo para esto hace algún tiempo. Funciona genial:

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

        }

    }
}

Se llama así:

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
fuente
3

Le publico un código que muestra una forma de resolver el problema. Como ejemplo, estoy leyendo un archivo. Puede usar este método para otra operación, pero necesita implementar el método kill () para que se interrumpa la operación principal.

Espero eso ayude


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!");
        }
    }
}

Saludos

elou
fuente
3

Aquí está mi clase de ayuda realmente simple de usar para ejecutar o llamar un código de Java :-)

Esto se basa en la excelente respuesta 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
fuente
2

El siguiente fragmento iniciará una operación en un hilo separado, luego esperará hasta 10 segundos para que se complete la operación. Si la operación no se completa a tiempo, el código intentará cancelar la operación y luego continuará felizmente. Incluso si la operación no se puede cancelar fácilmente, el subproceso principal no esperará a que finalice el subproceso secundario.

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
}

El getExecutorService()método puede implementarse de varias maneras. Si no tiene ningún requisito en particular, simplemente puede solicitar la Executors.newCachedThreadPool()agrupación de subprocesos sin límite superior en el número de subprocesos.

Markusk
fuente
¿Cuáles son las importaciones requeridas? ¿Qué son SomeClassy Future?
ADTC
2

Una cosa que no he visto mencionado es que matar hilos es generalmente una mala idea. Existen técnicas para hacer que los métodos roscados sean abortables de forma limpia , pero eso es diferente a simplemente matar un hilo después de un tiempo de espera.

El riesgo con lo que está sugiriendo es que probablemente no sepa en qué estado estará el hilo cuando lo mate, por lo que se arriesga a introducir inestabilidad. Una mejor solución es asegurarse de que su código enhebrado no se cuelgue o responda bien a una solicitud de cancelación.

Dan Puzey
fuente
Sin un contexto, una declaración como la suya suena demasiado restrictiva. En el ámbito académico, a menudo tengo la necesidad de probar algo hasta que se agota el tiempo de espera, y cuando ocurre, simplemente dejo todo el cálculo y registro que ocurrió el tiempo de espera. Probablemente es raro en la industria, pero aún así ...
Alessandro S.
@AlessandroS: ese es un punto razonable, aunque el OP solicitó "mejores soluciones", por lo que entendí que se prefería la robustez y la fiabilidad a la fuerza bruta.
Dan Puzey
2

Gran respuesta de BalusC:

pero solo para agregar que el tiempo de espera en sí mismo no interrumpe el hilo en sí. incluso si está comprobando con while (! Thread.interrupted ()) en su tarea. si desea asegurarse de que el subproceso esté detenido, también debe asegurarse de que se invoque future.cancel () cuando se capture la excepción de tiempo de espera.

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!!");
      }          
    } 
} 
Robocida
fuente
0

Creo que la respuesta depende principalmente de la tarea en sí.

  • ¿Está haciendo una tarea una y otra vez?
  • ¿Es necesario que el tiempo de espera interrumpa una tarea actualmente en ejecución inmediatamente después de que caduque?

Si la primera respuesta es sí y la segunda es no, podría mantenerlo tan simple como esto:

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 esto no es una opción, limite sus requisitos o muestre algún código.

sfussenegger
fuente
0

Estaba buscando un ExecutorService que pueda interrumpir todos los Runnables ejecutados por él, pero no encontré ninguno. Después de unas horas, creé uno de la siguiente manera. Esta clase se puede modificar para mejorar la robustez.

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

Uso:

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();
}
Tom
fuente
0

Ahora, me encuentro con un problema como este. Pasa a decodificar la imagen. El proceso de decodificación lleva demasiado tiempo para que la pantalla se mantenga negra. Agrego un controlador de tiempo: cuando el tiempo es demasiado largo, aparece el hilo actual. El siguiente es el 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
fuente
0

Yo tuve el mismo problema. Entonces se me ocurrió una solución simple como esta.

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

Garantiza que si el bloque no se ejecutó dentro del límite de tiempo. el proceso terminará y arrojará una excepción.

ejemplo:

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
fuente
0

En la solución dada por BalusC , el hilo principal permanecerá bloqueado durante el tiempo de espera. Si tiene un grupo de subprocesos con más de un subproceso, necesitará el mismo número de subprocesos adicionales que utilizará la llamada de bloqueo Future.get (tiempo de espera largo, unidad TimeUnit) para esperar y cerrar el subproceso si excede el período de tiempo de espera.

Una solución genérica a este problema es crear un decorador ThreadPoolExecutor que pueda agregar la funcionalidad de tiempo de espera. Esta clase de decorador debe crear tantos subprocesos como ThreadPoolExecutor, y todos estos subprocesos deben usarse solo para esperar y cerrar ThreadPoolExecutor.

La clase genérica debe implementarse como a continuación:

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

El decorador anterior se puede usar de la siguiente manera:

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
fuente