Requisito previo: subprocesos , subprocesos múltiples
La necesidad de Callable
Hay dos formas de crear hilos: una extendiendo la clase Thread y otra creando un hilo con un Runnable. Sin embargo, una característica que falta en Runnable es que no podemos hacer que un subproceso devuelva un resultado cuando termina, es decir, cuando se completa run(). Para admitir esta característica, la interfaz Callable está presente en Java.
Llamable vs Ejecutable
- Para implementar Runnable, se debe implementar el método run(), que no devuelve nada, mientras que para Callable, se debe implementar el método call(), que devuelve un resultado al finalizar. Tenga en cuenta que un subproceso no se puede crear con un Callable, solo se puede crear con un Runnable.
- Otra diferencia es que el método call() puede lanzar una excepción mientras que run() no puede.
Firma de método que debe anularse para implementar Callable.
public Object call() throws Exception;
Aquí está el código para un ejemplo de Callable, que devolverá un número aleatorio después de un retraso de alrededor de 0 a 4 segundos.
// Java program to illustrate Callable // to return a random number import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class CallableExample implements Callable { public Object call() throws Exception { // Create random number generator Random generator = new Random(); Integer randomNumber = generator.nextInt(5); // To simulate a heavy computation, // we delay the thread for some random time Thread.sleep(randomNumber * 1000); return randomNumber; } }
Futuro
Cuando se completa el método call(), la respuesta debe almacenarse en un objeto conocido por el subproceso principal, de modo que el subproceso principal pueda conocer el resultado que devolvió el subproceso. ¿Cómo almacenará el programa y obtendrá este resultado más adelante? Para esto, se puede usar un objeto Future. Piense en un futuro como un objeto que contiene el resultado; puede que no lo contenga en este momento, pero lo hará en el futuro (una vez que el Callable regrese). Por lo tanto, un futuro es básicamente una forma en que el subproceso principal puede realizar un seguimiento del progreso y el resultado de otros subprocesos. Para implementar esta interfaz, se deben anular 5 métodos, pero como el siguiente ejemplo usa una implementación concreta de la biblioteca, aquí solo se enumeran los métodos importantes.
Observe que Callable y Future hacen dos cosas diferentes: Callable es similar a Runnable, ya que encapsula una tarea que debe ejecutarse en otro subproceso, mientras que Future se usa para almacenar un resultado obtenido de un subproceso diferente. De hecho, el futuro también se puede hacer funcionar con Runnable, que es algo que quedará claro cuando los ejecutores entren en escena.
- public boolean cancel(boolean mayInterrupt): se utiliza para detener la tarea. Detiene la tarea si no ha comenzado. Si ha comenzado, interrumpe la tarea solo si mayInterrupt es verdadero.
- public Object get() lanza InterruptedException, ExecutionException: Se utiliza para obtener el resultado de la tarea. Si la tarea está completa, devuelve el resultado inmediatamente; de lo contrario, espera hasta que la tarea esté completa y luego devuelve el resultado.
- public boolean isDone(): devuelve verdadero si la tarea está completa y falso de lo contrario
Para crear el hilo, se requiere un Runnable. Para obtener el resultado se requiere un Futuro.
La biblioteca Java tiene el tipo concreto FutureTask, que implementa Runnable y Future, combinando ambas funcionalidades convenientemente.
Se puede crear una FutureTask proporcionando a su constructor un Callable. Luego, el objeto FutureTask se proporciona al constructor de Thread para crear el objeto Thread. Así, indirectamente, el hilo se crea con un Callable. Para mayor énfasis, tenga en cuenta que no hay forma de crear el hilo directamente con un Callable.
Aquí está el código para el ejemplo completo usando Callable y FutureTask.
// Java program to illustrate Callable and FutureTask // for random number generation import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class CallableExample implements Callable { public Object call() throws Exception { Random generator = new Random(); Integer randomNumber = generator.nextInt(5); Thread.sleep(randomNumber * 1000); return randomNumber; } } public class CallableFutureTest { public static void main(String[] args) throws Exception { // FutureTask is a concrete class that // implements both Runnable and Future FutureTask[] randomNumberTasks = new FutureTask[5]; for (int i = 0; i < 5; i++) { Callable callable = new CallableExample(); // Create the FutureTask with Callable randomNumberTasks[i] = new FutureTask(callable); // As it implements Runnable, create Thread // with FutureTask Thread t = new Thread(randomNumberTasks[i]); t.start(); } for (int i = 0; i < 5; i++) { // As it implements Future, we can call get() System.out.println(randomNumberTasks[i].get()); // This method blocks till the result is obtained // The get method can throw checked exceptions // like when it is interrupted. This is the reason // for adding the throws clause to main } } }
Producción:
4 2 3 3 0
Toda interacción con el subproceso después de que comience utiliza el objeto FutureTask, ya que implementa la interfaz Future. Por lo tanto, no hay necesidad de almacenar los objetos Thread. Usando el objeto FutureTask, uno puede cancelar la tarea, verificar si está completa o intentar obtener el resultado.
Aquí está el código usando solo Runnable.
// Java program to illustrate Runnable // for random number generation import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; class RunnableExample implements Runnable { // Shared object to store result private Object result = null; public void run() { Random generator = new Random(); Integer randomNumber = generator.nextInt(5); // As run cannot throw any Exception try { Thread.sleep(randomNumber * 1000); } catch (InterruptedException e) { e.printStackTrace(); } // Store the return value in result when done result = randomNumber; // Wake up threads blocked on the get() method synchronized(this) { notifyAll(); } } public synchronized Object get() throws InterruptedException { while (result == null) wait(); return result; } } // Code is almost same as the previous example with a // few changes made to use Runnable instead of Callable public class RunnableTest { public static void main(String[] args) throws Exception { RunnableExample[] randomNumberTasks = new RunnableExample[5]; for (int i = 0; i < 5; i++) { randomNumberTasks[i] = new RunnableExample(); Thread t = new Thread(randomNumberTasks[i]); t.start(); } for (int i = 0; i < 5; i++) System.out.println(randomNumberTasks[i].get()); } }
Salida de muestra
0 4 3 1 4 2
Referencias
- https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Callable.html
- https://docs.oracle.com/javase/7/docs/api/java/lang/Runnable.html
- https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/FutureTask.html
Este artículo es una contribución de Siddharth Sundar . Si te gusta GeeksforGeeks y te gustaría contribuir, también puedes escribir un artículo usando write.geeksforgeeks.org o enviar tu artículo por correo a review-team@geeksforgeeks.org. Vea su artículo que aparece en la página principal de GeeksforGeeks y ayude a otros Geeks.
Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
Publicación traducida automáticamente
Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA