Ejecutable vs. Ejecutable en Java

Descripción general

Desde los primeros días de Java, el multihilo ha sido un aspecto importante del lenguaje. Runnable es la interfaz principal proporcionada para representar tareas de subprocesos múltiples y Callable es una versión mejorada de Runnable que se agregó en Java 1.5.

En este artículo, exploraremos las diferencias y las aplicaciones de ambas interfaces.

Mecanismo de ejecución

Ambas interfaces están diseñadas para representar una tarea que se puede ejecutar mediante múltiples subprocesos. Las tareas ejecutables se pueden ejecutar usando la clase Thread o ExecutorService, mientras que las llamadas solo se pueden ejecutar usando esta última.

Valores de retorno

Echemos un vistazo más profundo a la forma en que estas interfaces manejan los valores de retorno.

3.1. Con Runnable

La interfaz Runnable es una interfaz funcional y tiene un único método run () que no acepta ningún parámetro ni devuelve ningún valor.

Esto es adecuado para situaciones en las que no estamos buscando un resultado de la ejecución del subproceso, por ejemplo, registro de eventos entrantes:

public interface Runnable { public void run();}

Entendamos esto con un ejemplo:

public class EventLoggingTask implements Runnable{ private Logger logger = LoggerFactory.getLogger(EventLoggingTask.class); @Override public void run() { logger.info("Message"); }}

En este ejemplo, el hilo solo leerá un mensaje de la cola y lo registrará en un archivo de registro. No se devuelve ningún valor de la tarea; la tarea se puede iniciar mediante ExecutorService:

public void executeTask() { executorService = Executors.newSingleThreadExecutor(); Future future = executorService.submit(new EventLoggingTask()); executorService.shutdown();}

En este caso, el objeto Futuro no tendrá ningún valor.

3.2. Con Callable

La interfaz Callable es una interfaz genérica que contiene un único método call (), que devuelve un valor genérico V:

public interface Callable<V> { V call() throws Exception;}

Echemos un vistazo al cálculo del factorial de un número:

public class FactorialTask implements Callable<Integer> { int number; // standard constructors public Integer call() throws InvalidParamaterException { int fact = 1; // ... for(int count = number; count > 1; count--) { fact = fact * count; } return fact; }}

El resultado del método call() se devuelve dentro de un objeto futuro:

@Testpublic void whenTaskSubmitted_ThenFutureResultObtained(){ FactorialTask task = new FactorialTask(5); Future<Integer> future = executorService.submit(task); assertEquals(120, future.get().intValue());}

Manejo de excepciones

Veamos qué tan adecuados son para el manejo de excepciones.

4.1. Con Runnable

Dado que la firma del método no tiene especificada la cláusula «throws», no hay forma de propagar más excepciones verificadas.

4.2. Con Callable

El método call() de Callable contiene la cláusula «lanza excepción» para que podamos propagar fácilmente las excepciones verificadas:

public class FactorialTask implements Callable<Integer> { // ... public Integer call() throws InvalidParamaterException { if(number < 0) { throw new InvalidParamaterException("Number should be positive"); } // ... }}

En caso de ejecutar un Ejecutable usando un ExecutorService, las excepciones se recopilan en el objeto Future, que se puede verificar haciendo una llamada al Futuro.método get (). Esto lanzará una excepción de ejecución, que envuelve la excepción original:

@Test(expected = ExecutionException.class)public void whenException_ThenCallableThrowsIt() { FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); Integer result = future.get().intValue();}

En la prueba anterior, se lanza la excepción ExecutionException al pasar un número no válido. Podemos llamar al método getCause () en este objeto de excepción para obtener la excepción comprobada original.

Si no hacemos la llamada al método get () de la clase Futura, entonces la excepción lanzada por el método call() no se informará, y la tarea seguirá marcada como completada:

@Testpublic void whenException_ThenCallableDoesntThrowsItIfGetIsNotCalled(){ FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); assertEquals(false, future.isDone());}

La prueba anterior pasará con éxito a pesar de que hemos lanzado una excepción para los valores negativos del parámetro a FactorialCallableTask.

Conclusión

En este artículo, hemos explorado las diferencias entre las interfaces Ejecutables y las ejecutables.

Como siempre, el código completo de este artículo está disponible en GitHub.

Comience con Spring 5 y Spring Boot 2, a través del curso Learn Spring:

>> ECHA UN VISTAZO AL CURSO

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

Previous post Nitrilo
Next post Julian Morris en ‘New Girl’ = La Mejor Idea de la historia