Einführung in die asynchrone Programmierung in Java
Moderne Anwendungen erfordern hohe Leistung und Reaktionsfähigkeit. Asynchrone Programmierung in Java ist ein Ansatz, der es ermöglicht, langwierige Operationen (z. B. Datenbankabfragen, API-Aufrufe, Dateilesen) auszuführen, ohne den Hauptausführungs-Thread zu blockieren. Anstatt auf den Abschluss einer Aufgabe zu warten, arbeitet das Programm weiter, und das Ergebnis wird verarbeitet, sobald es bereit ist.
In diesem Leitfaden werden wir die grundlegenden Konzepte der asynchronen Programmierung in Java behandeln, angefangen bei den Basistools bis hin zur modernen API CompletableFuture. Sie erfahren, wie Sie nicht-blockierenden Code erstellen, Fehler behandeln und asynchrone Aufgaben kombinieren.
Grundlegende Konzepte: Threads und ExecutorService
Threads
Der einfachste Weg, Code in Java asynchron auszuführen, ist die Erstellung eines neuen Threads. Das manuelle Erstellen von Threads ist jedoch ein teurer Vorgang, und bei einer großen Anzahl von Aufgaben kann dies zu Leistungsproblemen führen.
// Beispiel für die Erstellung eines ThreadsThread thread = new Thread(() -> { System.out.println("Asynchrone Aufgabe wird im Thread ausgeführt: " + Thread.currentThread().getName());});thread.start();System.out.println("Hauptthread arbeitet weiter");ExecutorService — Verwaltung eines Thread-Pools
Für eine effiziente Verwaltung von Threads stellt Java ExecutorService bereit. Dies ist ein Interface, mit dem Sie Thread-Pools erstellen, diese wiederverwenden und die Ausführung von Aufgaben steuern können.
import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;
public class ExecutorServiceExample { public static void main(String[] args) { // Erstellen eines Pools mit 4 Threads ExecutorService executor = Executors.newFixedThreadPool(4); // Senden einer Aufgabe zur Ausführung executor.submit(() -> { System.out.println("Aufgabe wird im Thread-Pool ausgeführt"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Aufgabe abgeschlossen"); }); // Beenden des Pools executor.shutdown(); }}Wichtige Arten von Thread-Pools:
newFixedThreadPool(n)— feste Anzahl von Threads.newCachedThreadPool()— dynamische Anzahl von Threads.newSingleThreadExecutor()— ein Thread für die sequentielle Ausführung von Aufgaben.newScheduledThreadPool(n)— für die Planung von Aufgaben mit Verzögerung.
Future und CompletableFuture
Interface Future
Future ist ein Interface, das das Ergebnis einer asynchronen Operation darstellt. Es ermöglicht zu überprüfen, ob die Aufgabe abgeschlossen ist, auf ihren Abschluss zu warten und das Ergebnis abzurufen.
import java.util.concurrent.*;
public class FutureExample { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executor = Executors.newSingleThreadExecutor(); Future<Integer> future = executor.submit(() -> { Thread.sleep(2000); return 42; }); System.out.println("Warte auf Ergebnis..."); Integer result = future.get(); // Blockiert den Thread bis zum Erhalt des Ergebnisses System.out.println("Ergebnis: " + result); executor.shutdown(); }}Nachteil von Future — die Methode get() blockiert den Thread. Für eine flexiblere Arbeit mit asynchronen Aufgaben wurde CompletableFuture entwickelt.
CompletableFuture — leistungsstarkes Tool für Asynchronität
CompletableFuture