When we need performing concurrent tasks in Java, we have available since the firsts versions, the Runnable interface. This interface have only method named run. That method must be implemented by participants that wants works with parallel tasks.
Look the signature this method:
1 2 3 4 |
|
Como observado acima através de sua a assinatura, não há uma maneira de recuperar o valor de uma operação ou ainda, nem mesmo é possível lançar uma exceção.
Para casos em que há tarefas que devolvem informações, será necessário o auxílio de um método ou uma propriedade compartilhada para recuperar armazenar o valor desejado após a execução de uma tarefa.
Conforme exemplo abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Não há problema algum com o uso do exemplo acima, contudo, existe uma solução mais adequada e viável para esse caso, através da utilização da interface nomeada Callable.
Callable é uma interface que foi disponibilizada na versão J2SE 5.0, ela oferece um método chamado call(). Através desse método, agora é possível recuperar valores retornados do tipo Object, ou mais especificamente, qualquer tipo devido à sua construção genericamente parametrizada.
1 2 3 4 |
|
Pelo fato de não ser possível trabalhar com Callable com auxílio da classe Thread, a qual implementa apenas Runnable, é usado ao invés disso, o suporte da interface ExecutorService para executar objetos do tipo Callable.
Através de ExecutorService, é utilizada o método submit, que aceita como parâmetro um Callable e retorna uma interface Future, responsável por disponibilizar resultados através de operações assíncronas, a assinatura do método submit é descrito abaixo:
1
|
|
Quando uma tarefa é executada através de Callable, sendo ela “submitada”, pelo método submit(), é retornado a interface Future e, através dela, é usado o método get, para recuperar os valores processados.
O método get é mutuamente excluído, até que a tarefa termine, semântica idêntica ao Join da classe Thread.
Uma demonstração do uso de Callable é exibido no código seguinte. Esse teste se baseia em um processo se dividir em duas linhas de execuções, e realizar a incrementação do valor e exibi-lo logo em seguida. Por exemplo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
No trecho de código acima, na linhas 25 é utilizado o método estático newFixedThreadPool(), da classe Executors. Esse método cria uma espécie de mina de Threads, e diz a quantidade de tarefas que serão executadas a um determinado momento. O argumento 1, diz que apenas uma Thread executará por vez.
Na linha 26, a variável c1 referencia um objeto que tem Callable a serem executados e na linha seguinte, uma outra variável(c2) referencia a mesma instância, para que seja possível demonstrar duas linhas de execuções, atuando na mesma tarefa.
No loop, as duas threads serão “submitadas” e executadas e seus valores recuperadas pelo método get() da interface Future e, no fim do loop, o serviço executor é finalizado com método shutdown(), o resultado da operação será:
1 2 3 4 5 6 7 8 9 10 |
|
De acordo o que foi passado para o método Executors.newFixedThreadPool( 1 ), apenas uma linha foi executada por tarefa. Caso o parâmetro seja alterado para 2, a saída será algo como:
1 2 3 4 5 6 7 8 9 10 |
|