Overview
Since the early days of Java, multithreading has been a major aspect of the language. Runnable é a interface principal fornecida para representar tarefas multi-threaded e Callable é uma versão melhorada do Runnable que foi adicionado em Java 1.5.
neste artigo, vamos explorar as diferenças e as aplicações de ambas as interfaces.
mecanismo de execução
ambas as interfaces são projetadas para representar uma tarefa que pode ser executada por vários threads. As tarefas executáveis podem ser executadas usando a classe de Thread ou ExecutorService, enquanto os Callables podem ser executados apenas usando o último.
valores de retorno
vamos dar uma olhada mais profunda na forma como essas interfaces lidam com os valores de retorno.
3.1. With Runnable
The Runnable interface is a functional interface and has a single run () method which doesn’t accept any parameters and does not return any values.
isto é adequado para situações em que não estamos à procura de um resultado da execução do tópico, por exemplo, o registo de eventos de entrada:
public interface Runnable { public void run();}
vamos entender isso com um exemplo:
public class EventLoggingTask implements Runnable{ private Logger logger = LoggerFactory.getLogger(EventLoggingTask.class); @Override public void run() { logger.info("Message"); }}
neste exemplo, o thread irá apenas ler uma mensagem da fila e registrá-la em um arquivo de log. Não existe nenhum valor devolvido da tarefa; a tarefa pode ser lançada usando o ExecutorService:
public void executeTask() { executorService = Executors.newSingleThreadExecutor(); Future future = executorService.submit(new EventLoggingTask()); executorService.shutdown();}
neste caso, o objeto futuro não terá qualquer valor.
3.2. Com Callable
a interface Callable é uma interface genérica que contém um método single call () – que devolve um valor Genérico V:
public interface Callable<V> { V call() throws Exception;}
vamos dar uma olhada no cálculo do fatorial de um 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; }}
o método do resultado da chamada() é devolvido dentro de um objeto futuro:
@Testpublic void whenTaskSubmitted_ThenFutureResultObtained(){ FactorialTask task = new FactorialTask(5); Future<Integer> future = executorService.submit(task); assertEquals(120, future.get().intValue());}
tratamento de exceções
vamos ver como eles são adequados para o tratamento de exceções.
4.1. With Runnable
Since the method signature does not have the” lances ” clause specified, there is no way to propagate further checked exceptions.
4.2. With Callable
Callable’s call () method contains “lances Exception” clause so we can easily propagate checked exceptions further:
public class FactorialTask implements Callable<Integer> { // ... public Integer call() throws InvalidParamaterException { if(number < 0) { throw new InvalidParamaterException("Number should be positive"); } // ... }}
Em caso de execução de um ExecutorService, as exceções são coletadas no objeto futuro, que pode ser verificado fazendo uma chamada para o futuro.método get (). Isto irá lançar uma excepção de execução – que envolve a excepção 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();}
no teste acima, a Execuçãoconcepção está sendo lançada como estamos passando um número inválido. Podemos chamar o método getCause() neste objeto de exceção para obter a exceção verificada original.
Se nós não fazemos a chamada para o método get() da Futura classe – em seguida, a exceção lançada pelo método call() não serão relatados de volta, e a tarefa ainda será marcada como concluída:
@Testpublic void whenException_ThenCallableDoesntThrowsItIfGetIsNotCalled(){ FactorialCallableTask task = new FactorialCallableTask(-5); Future<Integer> future = executorService.submit(task); assertEquals(false, future.isDone());}
O teste acima vai passar com êxito, mesmo que nós já lançada uma excepção para os valores negativos do parâmetro para FactorialCallableTask.
Conclusion
In this article, we’ve explored the differences between the Runnable and Callable interfaces.
como sempre, o código completo para este artigo está disponível no GitHub.
Começar com Mola 5 e Primavera de Inicialização 2, através do Aprender a Primavera curso:
>> confira O CURSO