A interface Java ExecutorService, java.util.concurrent.ExecutorService, representa um mecanismo de execução assíncrona que é capaz de executar tarefas simultaneamente em segundo plano. Neste Java ExecutorService tutorial vou explicar como criar um ExecutorService, como submeter tarefas para execução, como ver os resultados dessas tarefas, e como desligar o ExecutorService novamente quando for necessário.

Java ExecutorService Video Tutorial

Se preferir vídeo, tenho aqui uma introdução em vídeo:

Java ExecutorService Tutorial Vídeo - Parte 1br>Java ExecutorService Tutorial Vídeo - Parte 2

Task Delegation

Aqui está um diagrama que ilustra um tópico delegando uma tarefa a um Java ExecutorService para execução assíncrona:

Um tópico que delega uma tarefa a um ExecutorService para execução assíncrona.

br>>p>p> Um fio delegando uma tarefa a um ExecutorService para execução assíncrona.>br>

Uma vez que o fio tenha delegado a tarefa ao ExecutorService, o fio continua a sua própria execução independente da execução dessa tarefa. O ExecutorService então executa a tarefa simultaneamente, independentemente do fio que submeteu a tarefa.

Java ExecutorService Example

Antes de nos aprofundarmos demasiado no ExecutorService, vejamos um exemplo simples. Aqui está um simples Java ExecutorService exemplo:

ExecutorService executorService = Executors.newFixedThreadPool(10);executorService.execute(new Runnable() { public void run() { System.out.println("Asynchronous task"); }});executorService.shutdown();

Primeiro um ExecutorService é criado usando o método ExecutorsnewFixedThreadPool() de fábrica. Isto cria um conjunto de fios com 10 fios executando tarefas.

Segundo, uma implementação anónima do método Runnable interface é passada para o método execute(). Isto faz com que o Runnable seja executado por um dos fios do ExecutorService.

Verá vários outros exemplos de como utilizar o ExecutorService ao longo deste tutorial. Este exemplo serviu apenas para lhe dar uma rápida visão geral de como utilizar um ExecutorService para executar tarefas em segundo plano parece-se com.

Java ExecutorService Implementations

O Java ExecutorService é muito semelhante a um conjunto de fios. De facto, a implementação do pacote ExecutorService interface presente no pacote java.util.concurrent é uma implementação de “thread pool”. Se quiser compreender como a interface ExecutorService pode ser implementada internamente, leia o tutorial acima.

Uma vez que ExecutorService é uma interface, é necessário que as suas implementações sejam feitas de modo a fazer qualquer uso dela. O ExecutorService tem a seguinte implementação no pacote java.util.concurrent:

  • ThreadPoolExecutor
  • ScheduledThreadPoolExecutor

Criar um ExecutorService

Como se cria um ExecutorService depende da implementação que se utiliza. Contudo, pode usar o Executors classe de fábrica para criar ExecutorService instâncias também. Eis alguns exemplos de criação de um ExecutorService:

ExecutorService executorService1 = Executors.newSingleThreadExecutor();ExecutorService executorService2 = Executors.newFixedThreadPool(10);ExecutorService executorService3 = Executors.newScheduledThreadPool(10);

ExecutorService Usage

Há algumas formas diferentes de delegar tarefas para execução a um ExecutorService:

  • execute(Runnable)
  • li>submit(Runnable)li>submit(Callable)li>invokeAny(….)

  • invokeAll(…)

Vou dar uma vista de olhos a cada um destes métodos nas secções seguintes.

Execute Runnable

O método Java ExecutorServiceexecute(Runnable) toma um objecto java.lang.Runnable, e executa-o de forma assíncrona. Eis um exemplo de execução de um Runnable com um ExecutorService:

ExecutorService executorService = Executors.newSingleThreadExecutor();executorService.execute(new Runnable() { public void run() { System.out.println("Asynchronous task"); }});executorService.shutdown();

Não há forma de obter o resultado do objecto executado Runnable, se necessário. Terá de utilizar um Callable para isso (explicado nas secções seguintes).

Submit Runnable

O método Java ExecutorServicesubmit(Runnable) também leva um Runnable implementação, mas devolve um Future objecto. Este Future objecto pode ser utilizado para verificar se o Runnable terminou a execução.

Aqui está um Java ExecutorServicesubmit() exemplo:

Future future = executorService.submit(new Runnable() { public void run() { System.out.println("Asynchronous task"); }});future.get(); //returns null if the task has finished correctly.

O método submit() devolve um objecto Java Futuro que pode ser usado para verificar quando o Runnable estiver concluído.

Submit Callable

O método Java ExecutorServicesubmit(Callable) é semelhante ao método submit(Runnable) excepto que é necessário um método Java Callable em vez de um Runnable. A diferença precisa entre um Callable e um Runnable é explicada um pouco mais tarde.

O resultado do Callable pode ser obtido através do objecto Java Futuro devolvido pelo método submit(Callable). Aqui está um ExecutorServiceCallable exemplo:

Future future = executorService.submit(new Callable(){ public Object call() throws Exception { System.out.println("Asynchronous Callable"); return "Callable Result"; }});System.out.println("future.get() = " + future.get());

O exemplo de código acima irá produzir isto:

Asynchronous Callablefuture.get() = Callable Result

invokeAny()

O método invokeAny() leva uma colecção de Callable objectos, ou subinterfaces de Callable. Invocar este método não devolve um Future, mas devolve o resultado de um dos objectos de Callable. Não tem qualquer garantia sobre qual dos resultados do Callable obtém. Apenas um dos que terminam.

Se uma das tarefas Callable terminar, para que um resultado seja devolvido de invokeAny(), então o resto das instâncias Callable são canceladas.

Se uma das tarefas terminar (ou lançar uma excepção), o resto das Callable‘s são canceladas.

Aqui está um exemplo de código:

ExecutorService executorService = Executors.newSingleThreadExecutor();Set<Callable<String>> callables = new HashSet<Callable<String>>();callables.add(new Callable<String>() { public String call() throws Exception { return "Task 1"; }});callables.add(new Callable<String>() { public String call() throws Exception { return "Task 2"; }});callables.add(new Callable<String>() { public String call() throws Exception { return "Task 3"; }});String result = executorService.invokeAny(callables);System.out.println("result = " + result);executorService.shutdown();

Este exemplo de código imprimirá o objecto devolvido por um dos Callable‘s na colecção dada. Já tentei executá-lo algumas vezes, e o resultado muda. Por vezes é “Tarefa 1”, outras vezes “Tarefa 2” etc.

invokeAll()

O método invokeAll() invoca todos os Callable objectos que lhe são passados na colecção passada como parâmetro. O invokeAll() devolve uma lista de Future objectos através dos quais se podem obter os resultados das execuções de cada Callable.

Tenha em mente que uma tarefa pode terminar devido a uma excepção, pelo que pode não ter “tido sucesso”. Não há como num Future dizer a diferença.

Aqui está um exemplo de código:

ExecutorService executorService = Executors.newSingleThreadExecutor();Set<Callable<String>> callables = new HashSet<Callable<String>>();callables.add(new Callable<String>() { public String call() throws Exception { return "Task 1"; }});callables.add(new Callable<String>() { public String call() throws Exception { return "Task 2"; }});callables.add(new Callable<String>() { public String call() throws Exception { return "Task 3"; }});List<Future<String>> futures = executorService.invokeAll(callables);for(Future<String> future : futures){ System.out.println("future.get = " + future.get());}executorService.shutdown();

Runnable vs. Chamável

A interface Runnable é muito semelhante à interface Callable. A interface Runnable representa uma tarefa que pode ser executada simultaneamente por uma thread ou um ExecutorService. A Callable só pode ser executada por um ExecutorService. Ambas as interfaces têm apenas um único método. Há uma pequena diferença entre a interface Callable e Runnable. A diferença entre a interface Runnable e Callable é mais facilmente visível quando se vêem as declarações de interface.

Aqui está primeiro a Runnable declaração de interface:

public interface Runnable { public void run();}

E aqui está a Callable declaração de interface:

public interface Callable{ public Object call() throws Exception;}

A principal diferença entre o método Runnablerun() e o método Callablecall() método é que o call() método pode devolver um Object da chamada ao método. Outra diferença entre call() e run() é que call() pode lançar uma excepção, enquanto run() não pode (excepto excepções não verificadas – subclasses de RuntimeException).

Se precisar de submeter uma tarefa a uma interface Java ExecutorService e precisar de um resultado da tarefa, então precisa de fazer com que a sua tarefa implemente a interface Callable. Caso contrário, a sua tarefa pode apenas implementar a interface Runnable.

Cancelar tarefa

Pode cancelar uma tarefa (Runnable ou Callable) submetida a uma interface Java ExecutorService chamando o método cancel() no método Future devolvido quando a tarefa é submetida. O cancelamento da tarefa só é possível se a tarefa ainda não tiver começado a ser executada. Eis um exemplo de cancelamento de uma tarefa chamando o Future.cancel() method:

future.cancel();

ExecutorService Shutdown

Quando terminar de utilizar o Java ExecutorService deverá desligá-lo, para que os fios não continuem a correr. Se a sua aplicação for iniciada através de um método main() e o seu fio principal sair da sua aplicação, a aplicação continuará a correr se tiver um ExexutorService activo na sua aplicação. Os fios activos dentro deste ExecutorService impedem o encerramento da JVM.

shutdown()

para terminar os fios dentro do ExecutorService chama o seu método shutdown(). O ExecutorService não se desligará imediatamente, mas deixará de aceitar novas tarefas, e assim que todos os fios tiverem terminado as tarefas actuais, o ExecutorService desliga-se. Todas as tarefas submetidas ao ExecutorService antes de shutdown() ser chamado, são executadas. Eis um exemplo de execução de um Java ExecutorService shutdown:

executorService.shutdown();

shutdownNow()

Se quiser encerrar o método ExecutorService imediatamente, pode chamar o método shutdownNow(). Isto tentará parar imediatamente a execução de todas as tarefas, e salta todas as tarefas submetidas, mas não processadas. Não há garantias dadas sobre as tarefas de execução. Talvez elas parem, talvez a execução até ao fim. É uma tentativa de melhor esforço. Eis um exemplo de chamada ExecutorServiceshutdownNow:

executorService.shutdownNow();

awaitTermination()

O método ExecutorServiceawaitTermination() irá bloquear o fio que o chama até que o método ExecutorService se desligue completamente, ou até que ocorra um determinado período de tempo. O método awaitTermination() é tipicamente chamado após chamar shutdown() ou shutdownNow(). Aqui está um exemplo de chamada ExecutorServiceawaitTermination():

executorService.shutdown();executorService.awaitTermination(10_000L, TimeUnit.MILLISECONDS );

Deixe uma resposta

O seu endereço de email não será publicado. Campos obrigatórios marcados com *