ExecutorService.submit în Java

Trimite o sarcină executorului și returnează imediat un Future din care iei mai târziu rezultatul.

Sintaxă
<T> Future<T> submit(Callable<T> sarcina)
Future<?> submit(Runnable sarcina)
void execute(Runnable sarcina)

Ce face ExecutorService.submit

submit pune sarcina în coada executorului și revine imediat, fără să aștepte. Primești un Future: o „chitanță” pentru rezultatul care va veni. Rezultatul îl iei cu future.get(), care așteaptă până când sarcina se termină.

Sarcina poate fi un Callable (o lambda care returnează o valoare și poate arunca excepții verificate) sau un Runnable (o lambda fără valoare). Dacă lambda are return cu o valoare, Java alege Callable.

Diferența față de execute: dacă sarcina aruncă o excepție, submit o păstrează în Future și o primești la get(), învelită într-o ExecutionException. Cu execute, excepția se afișează pe ecran și se pierde.

Parametri

Parametru Descriere
sarcina Un Callable<T> (cu rezultat) sau un Runnable (fără rezultat).

Ce returnează

Un Future<T>. Pentru un Runnable, get() returnează null.

Exemple

Exemplul 1: Calcule în paralel, rezultate în ordine

Sarcinile rulează în paralel, dar rezultatele sunt citite din lista de Future în ordinea trimiterii.

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

public class Main {
    static int sumaNote(int[] note) throws InterruptedException {
        Thread.sleep(100);
        int s = 0;
        for (int n : note) s += n;
        return s;
    }

    public static void main(String[] args) throws Exception {
        String[] elevi = {"Ana", "Mihai", "Ioana"};
        int[][] note = {{9, 10, 8}, {7, 6, 9}, {10, 10, 9}};
        List<Future<Integer>> rezultate = new ArrayList<>();
        try (ExecutorService executor = Executors.newFixedThreadPool(3)) {
            for (int[] n : note) {
                rezultate.add(executor.submit(() -> sumaNote(n)));
            }
            for (int i = 0; i < elevi.length; i++) {
                System.out.println(elevi[i] + ": suma notelor = " + rezultate.get(i).get());
            }
        }
    }
}

Rezultat

Ana: suma notelor = 27
Mihai: suma notelor = 22
Ioana: suma notelor = 29

Exemplul 2: Excepția sarcinii ajunge la get()

import java.util.concurrent.ExecutionException;
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 InterruptedException {
        try (ExecutorService executor = Executors.newSingleThreadExecutor()) {
            Future<Integer> f = executor.submit(() -> Integer.parseInt("doi"));
            try {
                System.out.println(f.get());
            } catch (ExecutionException e) {
                System.out.println("Sarcina a eșuat: " + e.getCause());
            }
        }
    }
}

Rezultat

Sarcina a eșuat: java.lang.NumberFormatException: For input string: "doi"

Exemplul 3: Runnable: Future fără valoare

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 {
        try (ExecutorService executor = Executors.newSingleThreadExecutor()) {
            Future<?> f = executor.submit(() -> System.out.println("Trimit e-mailul către ana@exemplu.ro"));
            System.out.println("Rezultat: " + f.get());
        }
    }
}

Rezultat

Trimit e-mailul către ana@exemplu.ro
Rezultat: null

Greșeli frecvente

  • Apelezi get() imediat după fiecare submit, în aceeași buclă. Aștepți fiecare sarcină înainte s-o trimiți pe următoarea, deci nu mai rulează nimic în paralel. Trimite întâi toate sarcinile, apoi citește rezultatele.
  • Trimiți cu submit și nu apelezi niciodată get(). Dacă sarcina aruncă o excepție, nu o vezi nicăieri.

Comenzi înrudite

  • Future: Rezultatul unei sarcini care rulează pe alt fir: get() așteaptă și îl returnează, isDone() spune dacă s-a terminat.
  • 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().
  • ExecutorService.invokeAll: Trimite o listă de sarcini și așteaptă să se termine toate; returnează lista de Future în aceeași ordine.
  • CompletableFuture.supplyAsync: Pornește o sarcină pe alt fir și returnează imediat un CompletableFuture; supplyAsync are rezultat, runAsync nu.