El problema ejecutable
Ya está familiarizado con la interfaz Runnable y la clase Thread que la implementa. Recordemos cómo se ve esta interfaz:
public interface Runnable {
public abstract void run();
}
Tenga en cuenta que el tipo de devolución del método de ejecución es void . Pero, ¿qué pasa si necesitamos que nuestro hilo devuelva algún resultado de su trabajo en forma de un número, una cadena o algún otro objeto? Entonces debemos encontrar una solución. Algo como esto:
public class Fibonacci implements Runnable {
private final int index;
private int result;
public Fibonacci(int index) {
this.index = index;
}
@Override
public void run() {
int first = 0;
int second = 1;
if (index == 1) {
result = first;
} else if (index == 2) {
result = second;
} else {
for (int i = 0; i < index - 2; i++) {
int temp = second;
second += first;
first = temp;
}
result = second;
}
}
public static void printByIndex(int index) throws InterruptedException {
Fibonacci fibonacci = new Fibonacci(index);
Thread thread = new Thread(fibonacci);
thread.start();
thread.join();
System.out.println("Fibonacci number " + index + ": " + fibonacci.result);
}
}
Ejecutemos el siguiente método principal :
public static void main(String[] args) throws Exception {
Fibonacci.printByIndex(10);
}
La consola mostrará:
Este código tiene varios inconvenientes. Por ejemplo, como resultado de la llamada al método join , el subproceso principal se bloqueará mientras se ejecuta el método printByIndex .
Interfaz invocable
Ahora veamos la interfaz que Java nos proporciona lista para usar, que se puede usar como una alternativa a Runnable . Esta es la interfaz invocable :
public interface Callable<V> {
V call() throws Exception;
}
Como puede ver, al igual que Runnable , solo tiene un método. Este método tiene el mismo propósito que el método de ejecución : contiene el código que se ejecutará en un subproceso paralelo. En cuanto a las diferencias, eche un vistazo al valor de retorno. Ahora puede ser cualquier tipo que especifique al implementar la interfaz:
public class CurrentDate implements Callable<Long> {
@Override
public Long call() {
return new Date().getTime();
}
}
Otro ejemplo:
Callable<String> task = () -> {
Thread.sleep(100);
return "Done";
};
Aquí hay algo más útil: el método de llamada puede generar una excepción . Eso significa que, a diferencia del método de ejecución , en el método de llamada no tenemos que manejar las excepciones verificadas que ocurren dentro del método:
|
|
Interfaz futura
Otra interfaz que trabaja de cerca con Callable es Future . Future representa el resultado de cálculos asincrónicos (paralelos) (el valor devuelto por el método de llamada ). Le permite verificar si los cálculos están hechos, esperar a que terminen, obtener el resultado de los cálculos y más.
Métodos de la interfaz del futuro
-
boolean isDone() : este método devuelve verdadero si se realiza esta tarea (cálculo). Las tareas que finalizaron normalmente, finalizaron con una excepción o se cancelaron se consideran realizadas.
-
V get() : si es necesario, este método bloquea el subproceso que lo llamó y devuelve el resultado de los cálculos cuando finalizan.
-
V get (tiempo de espera prolongado, unidad de unidad de tiempo) : al igual que el método anterior, este método bloquea el subproceso que lo llamó, esperando el resultado, pero solo durante el tiempo especificado por los parámetros del método.
-
boolean cancel(boolean mayInterruptIfRunning) — este método intenta detener la ejecución de la tarea. Si la tarea aún no ha comenzado a ejecutarse, nunca se ejecutará. Si la tarea estaba en curso, el parámetro mayInterruptIfRunning determina si se intentará interrumpir el subproceso que ejecuta la tarea. Después de llamar al método cancel , el método isDone siempre devolverá true .
-
boolean isCancelled() : este método devuelve verdadero si la tarea se cancela antes de que finalice normalmente. El método siempre devolverá verdadero si el método de cancelación se llamó previamente y devolvió verdadero .
Ejemplo de código usando Callable y Future
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
public class Fibonacci implements Callable<Integer> {
private final int index;
public Fibonacci(int index) {
this.index = index;
}
@Override
public Integer call() {
int first = 0;
int second = 1;
if (index == 1) {
return first;
} else if (index == 2) {
return second;
} else {
for (int i = 0; i < index - 2; i++) {
int temp = second;
second += first;
first = temp;
}
return second;
}
}
public static Future<Integer> calculateAsync(int index) throws Exception {
Fibonacci fibonacci = new Fibonacci(index);
// The future object will represent the result of running the fibonacci task.
FutureTask<Integer> future = new FutureTask<>(fibonacci);
// Because the FutureTask class implements both the Future interface and the Runnable interface,
// you can pass instances of it to the Thread constructor
Thread thread = new Thread(future);
thread.start();
return future;
}
}
Ejecutemos el siguiente método principal :
public static void main(String[] args) throws Exception {
Map<Integer, Future<Integer>> tasks = new HashMap<>();
for (int i = 10; i < 20; i++) {
tasks.put(i, Fibonacci.calculateAsync(i));
}
for (Map.Entry<Integer, Future<Integer>> entry : tasks.entrySet()) {
Future<Integer> task = entry.getValue();
int index = entry.getKey();
int result;
// Check whether the task is done
if (task.isDone()) {
// Get the result of the calculations
result = task.get();
} else {
try {
// Wait another 100 milliseconds for the result of the calculations
result = task.get(100, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
// Interrupt the task
task.cancel(true);
System.out.println("Fibonacci number " + index + " could not be calculated in the allotted time.");
return;
}
}
System.out.println("Fibonacci number " + index + ": " + result);
}
}
La consola mostrará:
Número de Fibonacci 17: 987
Número de Fibonacci 18: 1597
Número de Fibonacci 19: 2584
Número de Fibonacci 10: 34 Número de
Fibonacci 11: 55 Número de Fibonacci 12:
89 Número de Fibonacci 13
: 144
Número de Fibonacci 14: 233
Número de Fibonacci 15: 377
GO TO FULL VERSION