Ce face ExecutorService
ExecutorService este o interfață pentru un „serviciu” care rulează sarcini pe fire de execuție (thread-uri). În loc să creezi singur câte un Thread pentru fiecare sarcină, trimiți sarcinile executorului, iar el le împarte pe firele lui. Creezi un executor cu metodele clasei Executors, de exemplu Executors.newFixedThreadPool(4).
Firele unui executor nu se opresc singure. Când nu mai ai sarcini de trimis, apelezi shutdown(): executorul nu mai primește sarcini noi, dar le termină pe cele deja primite. shutdown() nu așteaptă; revine imediat.
Pentru a aștepta terminarea sarcinilor apelezi awaitTermination(timp, unitate). Metoda blochează până când toate sarcinile se termină (returnează true) sau până expiră timpul (returnează false). shutdownNow() încearcă să întrerupă sarcinile care rulează și returnează lista celor care nu au pornit încă. Din Java 19 poți folosi executorul în try-with-resources: vezi ExecutorService.close.
Parametri
| Parametru | Descriere |
|---|---|
timp |
Cât să aștepte cel mult awaitTermination. |
unitate |
Unitatea pentru timp: TimeUnit.SECONDS, TimeUnit.MILLISECONDS etc. |
Ce returnează
shutdown() nu returnează nimic. awaitTermination returnează true dacă toate sarcinile s-au terminat la timp, altfel false.
Exemple
Exemplul 1: Trei sarcini pe două fire, apoi oprirea executorului
Rezultatele se scriu într-un tablou, fiecare sarcină în poziția ei, deci ordinea afișării nu depinde de fire.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) throws InterruptedException {
String[] orase = {"Iași", "Cluj", "Timișoara"};
int[] lungimi = new int[orase.length];
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < orase.length; i++) {
int poz = i;
executor.execute(() -> lungimi[poz] = orase[poz].length());
}
executor.shutdown();
boolean gata = executor.awaitTermination(5, TimeUnit.SECONDS);
System.out.println("Terminat la timp: " + gata);
for (int i = 0; i < orase.length; i++) {
System.out.println(orase[i] + " are " + lungimi[i] + " litere");
}
System.out.println("isShutdown: " + executor.isShutdown() + ", isTerminated: " + executor.isTerminated());
}
}
Rezultat
Terminat la timp: true
Iași are 4 litere
Cluj are 4 litere
Timișoara are 9 litere
isShutdown: true, isTerminated: true
Exemplul 2: awaitTermination cu timp prea scurt
Sarcina durează o secundă, dar așteptăm doar 100 ms: awaitTermination returnează false. Apoi shutdownNow() întrerupe sarcina.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.execute(() -> {
try {
Thread.sleep(1000);
System.out.println("Raport generat");
} catch (InterruptedException e) {
System.out.println("Raportul a fost întrerupt");
}
});
executor.shutdown();
System.out.println("Gata în 100 ms? " + executor.awaitTermination(100, TimeUnit.MILLISECONDS));
executor.shutdownNow();
System.out.println("Gata după shutdownNow? " + executor.awaitTermination(1, TimeUnit.SECONDS));
}
}
Rezultat
Gata în 100 ms? false
Raportul a fost întrerupt
Gata după shutdownNow? true
Exemplul 3: După shutdown nu mai primește sarcini
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
public class Main {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.shutdown();
try {
executor.execute(() -> System.out.println("nu se mai rulează"));
} catch (RejectedExecutionException e) {
System.out.println("Sarcina a fost respinsă: executorul este oprit");
}
}
}
Rezultat
Sarcina a fost respinsă: executorul este oprit
Greșeli frecvente
- Uiți
shutdown(). Firele unuinewFixedThreadPoolnu sunt fire daemon, deci programul nu se termină dupămain: rămâne „agățat”. - Crezi că
shutdown()așteaptă sarcinile. Revine imediat; pentru așteptare foloseșteawaitTerminationsauclose(). - Ignori rezultatul lui
awaitTermination. Dacă estefalse, sarcinile încă rulează și rezultatele lor nu sunt gata.
Comenzi înrudite
Executors: Clasa cu metode statice care creează executori: cu număr fix de fire, cu un singur fir, cu fire virtuale sau programate.ExecutorService.close: Oprește executorul și așteaptă să se termine toate sarcinile; permite folosirea executorului în try-with-resources.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.TimeUnit: Enumerarea unităților de timp (SECONDS, MINUTES…) folosită la așteptări; face conversii și pauze ușor de citit.