Atualizado: 02/11/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

Coordenação da Execução de Tarefas Assíncronas em Java

Em aplicações multithread, é comum precisar executar várias tarefas em paralelo — seja para obter o resultado da primeira tarefa que terminar com sucesso, seja para combinar os resultados de todas as tarefas concluídas. A interface ExecutorService oferece dois métodos para esse tipo de coordenação: invokeAll() e invokeAny().


Método invokeAll(): aguardando todas as tarefas

O método invokeAll() envia uma coleção de tarefas do tipo Callable para execução e bloqueia o código que faz a chamada até que todas as tarefas terminem. Ele retorna uma lista de objetos Future, que contêm o resultado ou o estado de cada tarefa, preservando a mesma ordem da lista de entrada.

Esse método possui duas versões:

<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

A primeira executa todas as tarefas sem limite de tempo. A segunda adiciona um tempo máximo de espera (timeout), após o qual as tarefas que ainda não terminaram são interrompidas.

Um possível inconveniente é que o método aguarda a finalização de todas as tarefas, mesmo que algumas falhem com exceção.


Exemplo com invokeAll()

O exemplo a seguir calcula fatoriais de vários números usando tarefas Callable executadas em threads virtuais:

import java.util.concurrent.*;
import java.util.List;
import java.util.ArrayList;

record FactorialResult(int number, int factorial) {}

class FactorialTask implements Callable<FactorialResult> {

    private int number;
    FactorialTask(int number) { this.number = number; }

    public FactorialResult call() throws Exception {

        if (number < 1) throw new Exception("Incorrect number: " + number + ". Number must be greater than 0");

        int result = 1;
        int n = number;
        while (n > 0) result *= n--;

        return new FactorialResult(number, result);
    }
}

class Program {

    public static void main(String[] args) {

        System.out.println("Main thread started...");

        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {

            List<Callable<FactorialResult>> tasks = new ArrayList<>();

            for (int i = -1; i < 4; i++) tasks.add(new FactorialTask(i));

            List<Future<FactorialResult>> results = executor.invokeAll(tasks);

            for (Future<FactorialResult> result : results) {
                if (result.state() == Future.State.SUCCESS) {
                    FactorialResult factorialResult = result.resultNow();
                    System.out.printf("Factorial of %d: %d\n",
                            factorialResult.number(), factorialResult.factorial());
                } else {
                    var ex = result.exceptionNow();
                    System.out.println("Factorial exception: " + ex.getMessage());
                }
            }

        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }

        System.out.println("Main thread finished...");
    }
}

Neste exemplo:

  • FactorialResult é um record que armazena o número e o resultado do cálculo do fatorial.
  • FactorialTask implementa Callable<FactorialResult> e calcula o fatorial do número recebido no construtor.
  • O ExecutorService é criado com Executors.newVirtualThreadPerTaskExecutor(), que executa cada tarefa em uma thread virtual.

O método invokeAll() envia todas as tarefas e bloqueia até que todas terminem. Depois, percorre a lista de Future retornados:

  • Se o estado da tarefa for SUCCESS, o método resultNow() retorna o resultado.
  • Se a tarefa falhou, exceptionNow() retorna a exceção correspondente.

Saída esperada:

Main thread started...
Factorial exception: Incorrect number: -1. Number must be greater than 0
Factorial exception: Incorrect number: 0. Number must be greater than 0
Factorial of 1: 1
Factorial of 2: 2
Factorial of 3: 6
Main thread finished...

Método invokeAny(): aguardando apenas uma tarefa

O método invokeAny() também recebe uma coleção de tarefas Callable, mas retorna o resultado da primeira tarefa que terminar com sucesso. As demais tarefas são canceladas automaticamente.

Suas duas versões são:

<T> T invokeAny(Collection<? extends Callable<T>> tasks)
<T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit)

A primeira aguarda indefinidamente até uma tarefa ser concluída com sucesso. A segunda define um tempo máximo de espera. Se nenhuma tarefa terminar com sucesso dentro do tempo, ou se todas falharem, é lançada uma exceção ExecutionException.


Exemplo com invokeAny()

import java.util.concurrent.*;
import java.util.List;
import java.util.ArrayList;

record FactorialResult(int number, int factorial) {}

class FactorialTask implements Callable<FactorialResult> {

    private int number;
    FactorialTask(int number) { this.number = number; }

    public FactorialResult call() throws Exception {

        if (number < 1) throw new Exception("Incorrect number: " + number + ". Number must be greater than 0");

        int result = 1;
        int n = number;
        while (n > 0) result *= n--;

        return new FactorialResult(number, result);
    }
}

class Program {

    public static void main(String[] args) {

        System.out.println("Main thread started...");

        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {

            List<Callable<FactorialResult>> tasks = new ArrayList<>();

            for (int i = -1; i < 4; i++) tasks.add(new FactorialTask(i));

            FactorialResult factorialResult = executor.invokeAny(tasks);
            System.out.printf("Factorial of %d: %d\n",
                    factorialResult.number(), factorialResult.factorial());

        } catch (Exception ex) {
            System.out.println(ex.getMessage());
        }

        System.out.println("Main thread finished...");
    }
}

Nesse caso, todas as tarefas são iniciadas simultaneamente. O método invokeAny() retorna o resultado da primeira tarefa concluída com sucesso, cancelando as demais.

Possível saída:

Main thread started...
Factorial of 3: 6
Main thread finished...

O resultado depende da ordem de término das tarefas, portanto pode variar a cada execução.

Se todas as tarefas falharem (por exemplo, se os números forem inválidos), uma exceção é lançada e capturada no bloco catch:

Main thread started...
java.lang.Exception: Incorrect number: -1. Number must be greater than 0
Main thread finished...

Resumo

  • invokeAll() executa todas as tarefas e retorna uma lista de Future após a conclusão de todas.
  • invokeAny() retorna o resultado da primeira tarefa concluída com sucesso e cancela as demais.
  • Ambas as funções bloqueiam a thread que faz a chamada até o término das tarefas.
  • Em invokeAll(), cada Future pode ser consultado para obter o resultado (resultNow()) ou exceção (exceptionNow()).
  • O ExecutorService pode ser criado com diferentes métodos da classe Executors, incluindo o uso de threads virtuais.
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com