ExecutorService.invokeAll în Java

Trimite o listă de sarcini și așteaptă să se termine toate; returnează lista de Future în aceeași ordine.

Sintaxă
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> sarcini) throws InterruptedException
<T> T invokeAny(Collection<? extends Callable<T>> sarcini) throws InterruptedException, ExecutionException

Ce face ExecutorService.invokeAll

invokeAll primește o colecție de Callable, le trimite pe toate executorului și așteaptă până se termină toate. Lista de Future returnată are aceeași ordine ca lista de sarcini, deci get() pe fiecare element nu mai blochează.

Varianta cu timp, invokeAll(sarcini, timp, unitate), anulează sarcinile care nu s-au terminat în timpul dat. Metoda înrudită invokeAny returnează rezultatul primei sarcini care reușește și le anulează pe celelalte.

Parametri

Parametru Descriere
sarcini O colecție (de obicei List) de Callable<T>.

Ce returnează

List<Future<T>>, în ordinea sarcinilor; toate sunt terminate (isDone() este true).

Exemple

Exemplul 1: Prețul total al unor coșuri de cumpărături

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {
    public static void main(String[] args) throws Exception {
        List<Callable<Integer>> sarcini = List.of(
            () -> 12 + 7 + 30,
            () -> 99 + 1,
            () -> 45 * 2
        );
        try (ExecutorService executor = Executors.newFixedThreadPool(3)) {
            List<Future<Integer>> rezultate = executor.invokeAll(sarcini);
            int nr = 1;
            for (Future<Integer> f : rezultate) {
                System.out.println("Coșul " + nr++ + ": " + f.get() + " lei (gata: " + f.isDone() + ")");
            }
        }
    }
}

Rezultat

Coșul 1: 49 lei (gata: true)
Coșul 2: 100 lei (gata: true)
Coșul 3: 90 lei (gata: true)

Exemplul 2: invokeAll cu limită de timp

Sarcina lentă nu se termină în 500 ms, deci este anulată; isCancelled() arată asta.

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

public class Main {
    public static void main(String[] args) throws Exception {
        Callable<String> rapid = () -> "Iași: 14°C";
        Callable<String> lent = () -> {
            Thread.sleep(5000);
            return "Cluj: 11°C";
        };
        try (ExecutorService executor = Executors.newFixedThreadPool(2)) {
            List<Future<String>> rezultate = executor.invokeAll(List.of(rapid, lent), 500, TimeUnit.MILLISECONDS);
            for (Future<String> f : rezultate) {
                System.out.println(f.isCancelled() ? "anulată (prea lentă)" : f.get());
            }
        }
    }
}

Rezultat

Iași: 14°C
anulată (prea lentă)

Exemplul 3: invokeAny: primul răspuns câștigă

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main {
    static Callable<String> server(String nume, int ms) {
        return () -> {
            Thread.sleep(ms);
            return "răspuns de la " + nume;
        };
    }

    public static void main(String[] args) throws Exception {
        try (ExecutorService executor = Executors.newFixedThreadPool(3)) {
            String r = executor.invokeAny(List.of(server("București", 600), server("Frankfurt", 100), server("Londra", 900)));
            System.out.println(r);
        }
    }
}

Rezultat

răspuns de la Frankfurt

Greșeli frecvente

  • Dai lui invokeAll o listă de Runnable. Primește doar Callable; o lambda fără return nu se potrivește.
  • Uiți că invokeAll blochează. Dacă vrei să faci altceva cât timp rulează sarcinile, folosește submit pentru fiecare.

Comenzi înrudite

  • ExecutorService.submit: Trimite o sarcină executorului și returnează imediat un Future din care iei mai târziu rezultatul.
  • Future: Rezultatul unei sarcini care rulează pe alt fir: get() așteaptă și îl returnează, isDone() spune dacă s-a terminat.
  • CompletableFuture.allOf: Returnează un CompletableFuture care se termină când s-au terminat toate cele primite; anyOf, când s-a terminat primul.
  • ExecutorService: Un grup de fire de execuție care primește sarcini și le rulează; se oprește cu shutdown() și se așteaptă cu awaitTermination().