Java ExecutorService: Wait for All Tasks to Complete [Guida Completa]

java-executorservice-wait-for-all-tasks-to-complete

In Java, l’uso di ExecutorService rappresenta una soluzione efficiente per gestire thread multipli. Tuttavia, una delle esigenze più comuni nello sviluppo multithread è sapere come attendere il completamento di tutti i task. Ecco come usare Java ExecutorService per aspettare che tutti i task siano completati, con esempi pratici e ottimizzati per la produzione.

java-executorservice-wait-for-all-tasks-to-complete

Cos’è ExecutorService in Java

ExecutorService è un’interfaccia del pacchetto java.util.concurrent. Permette di gestire l’esecuzione di task asincroni attraverso un pool di thread. Offre metodi per sottomettere task e per controllare l’esecuzione, la terminazione e la gestione dei risultati.


Metodi per Aspettare il Completamento di Tutti i Task

Esistono più approcci per far sì che il tuo codice attenda il completamento di tutti i task eseguiti tramite un ExecutorService.

1. Usare shutdown() e awaitTermination()

È il metodo più semplice per attendere la fine dell’esecuzione di tutti i task.

ExecutorService executor = Executors.newFixedThreadPool(5);

for (int i = 0; i < 10; i++) {
    executor.submit(() -> {
        // Task da eseguire
        System.out.println("Task eseguito da: " + Thread.currentThread().getName());
    });
}

executor.shutdown(); // Rifiuta nuovi task

try {
    if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
        executor.shutdownNow(); // Forza la terminazione se i task non finiscono
    }
} catch (InterruptedException e) {
    executor.shutdownNow();
    Thread.currentThread().interrupt();
}

Pro:

  • Semplice da implementare
  • Ideale per task che non restituiscono risultati

Contro:

  • Non consente di raccogliere i risultati dei task

2. Usare invokeAll() per Ottenere i Risultati e Aspettare

Se hai bisogno dei risultati da più Callable, invokeAll() è la soluzione giusta. Aspetta automaticamente che tutti i task siano completati prima di restituire i risultati.

ExecutorService executor = Executors.newFixedThreadPool(5);

List<Callable<String>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    int index = i;
    tasks.add(() -> "Risultato task " + index);
}

try {
    List<Future<String>> futures = executor.invokeAll(tasks);

    for (Future<String> future : futures) {
        System.out.println(future.get());
    }
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
} finally {
    executor.shutdown();
}

Pro:

  • Blocca fino a quando tutti i task sono completati
  • Permette di raccogliere i risultati facilmente

Contro:

  • Non adatto per Runnable (usa Callable)

3. Usare CompletionService per Gestire Task Completati Singolarmente

Se vuoi elaborare ogni task man mano che viene completato, puoi usare CompletionService.

ExecutorService executor = Executors.newFixedThreadPool(5);
CompletionService<String> completionService = new ExecutorCompletionService<>(executor);

for (int i = 0; i < 10; i++) {
    int index = i;
    completionService.submit(() -> "Completato: " + index);
}

for (int i = 0; i < 10; i++) {
    try {
        Future<String> result = completionService.take(); // Blocca finché un task non è completato
        System.out.println(result.get());
    } catch (InterruptedException | ExecutionException e) {
        e.printStackTrace();
    }
}

executor.shutdown();

Pro:

  • Ottimo per task lunghi e asincroni
  • Risultati disponibili non appena ogni task termina

Contro:

  • Richiede una gestione più complessa

4. Soluzione con CountDownLatch

Per una gestione manuale della sincronizzazione, CountDownLatch può essere una scelta efficace.

ExecutorService executor = Executors.newFixedThreadPool(5);
CountDownLatch latch = new CountDownLatch(10); // Numero di task

for (int i = 0; i < 10; i++) {
    executor.submit(() -> {
        try {
            // Simula lavoro
            Thread.sleep(1000);
            System.out.println("Task completato da: " + Thread.currentThread().getName());
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            latch.countDown(); // Decrementa il contatore
        }
    });
}

try {
    latch.await(); // Aspetta che il contatore arrivi a zero
} catch (InterruptedException e) {
    Thread.currentThread().interrupt();
}

executor.shutdown();

Pro:

  • Pieno controllo sul completamento
  • Funziona anche con Runnable

Contro:

  • Più codice da gestire

Best Practices per Aspettare il Completamento dei Task

  • Evita il polling attivo: non usare cicli infiniti per verificare se i task sono terminati.
  • Imposta timeout: usa awaitTermination() con un timeout per evitare attese infinite.
  • Gestisci le eccezioni: usa blocchi try-catch per prevenire errori inattesi.
  • Chiudi sempre il pool: invoca shutdown() o shutdownNow() per liberare risorse.

Differenze tra shutdown(), shutdownNow() e awaitTermination()

MetodoComportamento
shutdown()Interrompe l’accettazione di nuovi task. I task attivi continuano.
shutdownNow()Prova a interrompere tutti i task in esecuzione.
awaitTermination()Attende il completamento dei task entro un timeout specificato.

Quando Usare Ogni Metodo

  • Usa invokeAll() se hai bisogno dei risultati di tutti i task.
  • Usa awaitTermination() se ti interessa solo che tutti i task finiscano.
  • Usa CompletionService per task asincroni con risposta immediata.
  • Usa CountDownLatch quando vuoi gestire la sincronizzazione in modo personalizzato.

Conclusione

Sapere come usare ExecutorService per attendere il completamento di tutti i task in Java è fondamentale nello sviluppo di applicazioni concorrenti. Ogni metodo presentato ha i suoi vantaggi. La scelta dipende dalla natura dei task e dalla logica della tua applicazione.

Mantieni sempre un controllo rigoroso delle risorse, gestisci le eccezioni e assicurati di chiudere il ExecutorService per evitare memory leak o blocchi.

By Mario Lattice

Appassionato e sempre entusiasta della tecnologia e di poterla usare. Amo scrivere per raccontare le ultime novità tecnologiche.

Puoi leggere

No widgets found. Go to Widget page and add the widget in Offcanvas Sidebar Widget Area.