No me voy a poner a hablar del futuro de Java...
Supongamos que queremos correr un proceso pero por un cierto tiempo, si demora más de ese tiempo debemos lanzar un error y si no retornar el valor.
Para esto podemos utilizar java.util.concurrent.Future.
En pocas palabras, la clase Future representa un resultado futuro de un cálculo asincrónico, un resultado que eventualmente aparecerá en el Future una vez que se complete el procesamiento.
Los métodos de ejecución prolongada son buenos candidatos para el procesamiento asincrónico y la interfaz Future. Esto nos permite ejecutar algún otro proceso mientras esperamos que se complete la tarea encapsulada en Future.
Algunos ejemplos de operaciones que aprovecharían la naturaleza asincrónica de Future son:
- Procesos computacionales intensivos (cálculos matemáticos y científicos).
- manipular grandes estructuras de datos (big data)
- llamadas a métodos remotos (descarga de archivos, desguace HTML, servicios web).
Veamos como podemos hacer un Future :
public class SquareCalculator {
private ExecutorService executor
= Executors.newSingleThreadExecutor();
public Future<Integer> calculate(Integer input) {
return executor.submit(() -> {
Thread.sleep(1000);
return input * input;
});
}
}
En el ejemplo, crearmos una clase muy simple que calcula el cuadrado de un entero. Esto definitivamente no encaja en la categoría de métodos de "ejecución prolongada", pero ponemos una llamada Thread.sleep () para que dure 1 segundo en completarse.
Ya entendido el Future, veamos como nos puede ayudar para ejecutar procesos un cierto tiempo.
El método get () bloqueará la ejecución hasta que se complete la tarea. get() tiene una versión sobrecargada que toma un tiempo de espera y un TimeUnit como argumentos:
Integer result = future.get(500, TimeUnit.MILLISECONDS);
Si esto tarda más de 500 milisegundos, se lanzará TimeoutException. Veamos un ejemplo completo:
// The synchronous method
timeoutFuture = new SquareCalculator().calculate(10);
try {
timeoutFuture.get(1000, TimeUnit.MILLIS);
} catch (TimeoutException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
Y listo! En el catch podemos informar que no termino en el tiempo requerido.
Lo malo del método get es que es bloqueante y muchas veces no queremos bloquear nuestra ejecución para esperar. Por lo tanto podemos utilizar el método isDone :
Future<Integer> future = new SquareCalculator().calculate(10);
while(!future.isDone()) {
System.out.println("Calculating...");
Thread.sleep(300);
}
Integer result = future.get();
Future, tal vez no es la mejor opción para procesos asincronos complejos pero para estas cositas viene muy bien y es muy fácil de usar.