2012-08-14 18 views
9

Algunas veces la duración de una tarea repetida es más larga que su período (en mi caso, esto puede suceder durante horas) . Piense en una tarea repetida que tarda 7 minutos en ejecutarse y está programada para ejecutarse cada 10 minutos, pero a veces lleva 15 minutos por cada ejecución durante unas horas seguidas.Programe un ejecutable repetitivo de subproceso simple en java, pero omita la ejecución actual si la ejecución anterior no finaliza

Las clases Timer y ScheduledThreadPoolExecutor tienen un método scheduleAtFixedRate que generalmente se usa para este tipo de funcionalidad. Sin embargo, ambos tienen la característica de que "intentan ponerse al día cuando se quedan atrás". En otras palabras, si un temporizador se retrasa con algunas ejecuciones, crea una cola de trabajo en la que se trabajará continuamente hasta que recupere el número de ejecuciones que habría sucedido si ninguna de las tareas hubiera llevado más tiempo. el período especificado. Quiero evitar este comportamiento omitiendo la ejecución actual si la ejecución anterior no está completa.

Tengo una solución que implica jugar con el método afterExecution de un ejecutor agrupado, recalcular un retraso y reprogramar el ejecutable con el nuevo retraso, pero me preguntaba si hay una manera más simple, o si esta funcionalidad ya existe en una biblioteca común en alguna parte. Sé acerca de la programación con un retraso fijo en lugar de un período fijo, pero esto no funcionará para mí, ya que es importante tratar de ejecutar las tareas en los horarios establecidos. ¿Hay opciones más simples que mi solución afterExecution?

+3

Lo más sencillo es verificar su tarea si ya hay otra instancia ejecutándose y, de ser así, disparar inmediatamente. –

+0

@ Hot Licks: ScheduledExecutorService en realidad no los ejecuta en paralelo. –

+0

Entonces, ¿cuál es el requisito clave aquí? ¿Que solo y siempre comienza en ** a ** intervalo de 10 minutos, independientemente de si se perdió uno? Si es así, no estás hablando de una tasa. –

Respuesta

13

Creo que lo que desea es que la tarea de ejecución prolongada no se ejecute en el propio ScheduledExecutorService, sino en un hilo de fondo. Luego, la tarea de tasa fija siempre se completará rápidamente, ya que solo se usa para verificar si se inicia la tarea real en segundo plano (o no, si aún se está ejecutando desde la última vez).

ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); 
final Runnable actualTask = null; 

executorService.scheduleAtFixedRate(new Runnable() { 
    private final ExecutorService executor = Executors.newSingleThreadExecutor(); 
    private Future<?> lastExecution; 
    @Override 
    public void run() { 
     if (lastExecution != null && !lastExecution.isDone()) { 
      return; 
     } 
     lastExecution = executor.submit(actualTask); 
    } 
}, 10, 10, TimeUnit.MINUTES); 
+0

Ya mencioné esta opción en mi pregunta. – jonderry

+0

@jonderry: Perdón, extrañé tu última oración. Tengo un par de ideas más, no estoy seguro si serán más limpias. –

+0

@jonderry: Ok, creo que la última sugerencia que publiqué es buena. Limpié mi respuesta, puedes ver el historial de edición para las otras sugerencias. –

1

Haga una tercera clase, digamos llamada Coordinador. El coordinador tiene un método startRunning() sincronizado que establece isRunning como verdadero y devuelve verdadero si otro hilo no se estaba ejecutando. También debe haber un método stopRunning sincronizado que establezca isRunning en falso. Devuelve verdadero si ya se está ejecutando un ejecutable. Realiza una única instancia de esta clase y pasa una referencia a todos los ejecutables que construye. En el método de ejecución del ejecutable, primero llama a startRunning y verifica la declaración para verificar que no se está ejecutando otra. Asegúrese de poner el código en run() en try-finally y llame a stopRunning desde el bloque finally.

Cuestiones relacionadas