15 Planificación del inicio de ejecución de una tarea¶
Planificación del inicio de ejecución de una tarea¶
Para poder planificar el momento en el que debe ejecutarse una tarea en el ejecutor, la interfaz ScheduledExecutorService
proporciona el método schedule(runnable, delay, timeUnit)
, en el que la tarea se pasa en forma de Runnable
. Recibe además el periodo de tiempo que queremos que el ejecutor espere antes de ejecutarla respecto al momento en el que se recibe, a lo que se conoce como delay, y la unidad de tiempo en la que debe medirse dicha cantidad de tiempo (timeUnit). Este método retorna un ScheduledFuture<?>
que representa la tarea asíncrona enviada que no va a retornar ningún valor, ya que se trata de un runnable.
ScheduledFuture<V>
es una interfaz que extiende de Future<V>
, por lo que incluye toda la funcionalidad que ya conocemos para obtener el valor o cancelar la tarea, pero además extiende de la interfaz Delayed
, que proporciona el método getDelay(timeUnit)
para poder obtener el delay restante para iniciar la ejecución de la tarea asociada.
Si la tarea que queremos planificar es un Callable<V>
, podemos usar el método schedule(callableV, delay, timeUnit)
, que recibe el callable y los parámetros delay y timeUnit que estudiamos en el método anterior. La llamada a este método retornará un ScheduledFuture<V>
que podremos usar para obtener el valor retornado por el callable, cancelar la tarea e incluso consultar cuánto tiempo falta para su ejecución.
Un aspecto muy importante de ambos métodos es que el valor de delay debe ser expresado respecto al momento del envío de la tarea al ejecutor, por lo que si queremos que la tarea se ejecute en un momento concreto, deberemos calcular la diferencia entre dicho momento y la fecha/hora actual y usarla como delay.
El ejecutor garantiza que la tarea no comenzará su ejecución antes de que haya transcurrido el delay especificado, ya que su ejecución no estará habilitada hasta que no pase dicho lapso de tiempo. Sin embargo, una vez habilitada la posibilidad de ejecución, no hay una garantía del momento exacto en el que ésta comenzará. Si varias tareas son habilitadas para ejecución exactamente en el mismo momento, se ejecutarán en el orden en el que fueron enviadas.
Proyecto Schedule¶
En este proyecto crearemos un ejecutor ScheduledThreadPoolExecutor
con uno sólo hilo al que enviaremos , de manera que las tareas se ejecuten en orden y con un retardo de 1 segundo entre ellas. La tarea en sí será simulada con la escritura de un mensaje y retornará una cadena con un saludo.
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.*;
class Main {
public static void main(String[] args) {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
ScheduledExecutorService scheduledExecutor =
Executors.newScheduledThreadPool(5);
GreetTask greetTask = new GreetTask("Hello");
ScheduledFuture<?> greetScheduledFuture = scheduledExecutor.schedule(greetTask, 5, TimeUnit.SECONDS);
System.out.printf("%s -> %s - Greet task sent. Still %d seconds left\n",
Thread.currentThread().getName(),
dateTimeFormatter.format(LocalTime.now()),
greetScheduledFuture.getDelay(TimeUnit.SECONDS));
int number = -10;
// Try with
//int number = -1;
int factorialTaskDelay = 10;
// Try with
// int factorialTaskDelay = 1;
FactorialTask factorialTask = new FactorialTask(number);
ScheduledFuture<Integer> factorialScheduledFuture =
scheduledExecutor.schedule(factorialTask, factorialTaskDelay, TimeUnit.SECONDS);
System.out.printf("%s -> %s - factorial(%d) task sent\n",
Thread.currentThread().getName(),
dateTimeFormatter.format(LocalTime.now()),
number);
System.out.printf("%s -> %s - Waiting for factorial(%d) result. Still %d seconds left\n",
Thread.currentThread().getName(),
dateTimeFormatter.format(LocalTime.now()),
number,
factorialScheduledFuture.getDelay(TimeUnit.SECONDS));
try {
Integer factorial = factorialScheduledFuture.get();
System.out.printf("%s -> %s - factorial(%d) = %d\n",
Thread.currentThread().getName(),
dateTimeFormatter.format(LocalTime.now()),
number, factorial);
} catch (InterruptedException ignored) {
} catch (ExecutionException e) {
System.out.printf("%s -> %s - factorial(%d) launched an exception\n",
Thread.currentThread().getName(),
dateTimeFormatter.format(LocalTime.now()),
number);
} finally {
scheduledExecutor.shutdown();
}
}
}
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;
class GreetTask implements Runnable {
private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
private final String greet;
@SuppressWarnings("SameParameterValue")
GreetTask(String greet) {
this.greet = greet;
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(6);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.printf("%s -> %s - %s\n",
Thread.currentThread().getName(),
dateTimeFormatter.format(LocalTime.now()),
greet);
}
}
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
class FactorialTask implements Callable<Integer> {
private final int number;
private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
FactorialTask(int number) {
this.number = number;
}
@Override
public Integer call() throws InterruptedException {
Integer result = factorial(number);
System.out.printf("%s -> %s - factorial(%d) calculated\n",
Thread.currentThread().getName(),
dateTimeFormatter.format(LocalTime.now()),
number);
return result;
}
private Integer factorial(int number) throws InterruptedException {
if (number < 0) throw new IllegalArgumentException("Number can't be negative");
int factorial = 1;
for (int i = 2; i <= number; i++) {
factorial *= i;
TimeUnit.MILLISECONDS.sleep(20);
}
TimeUnit.SECONDS.sleep(2);
return factorial;
}
}