Aufgabenwarteschlangen in Java: ExecutorService und ThreadPoolExecutor
In Multithread-Anwendungen in Java ist die Verwaltung von Aufgaben und deren Ausführung eine der zentralen Aufgaben. Anstatt für jede Aufgabe einen neuen Thread zu erstellen (was teuer und ineffizient ist), verwenden Entwickler Aufgabenwarteschlangen (Task Queues) in Kombination mit Thread-Pools. Dies ermöglicht die Kontrolle über die Anzahl gleichzeitig laufender Threads, deren Wiederverwendung und die reibungslose Verarbeitung von Lastspitzen.
Die Grundlage für die Arbeit mit Aufgabenwarteschlangen in Java bildet das Framework java.util.concurrent, das in Java 5 eingeführt wurde. Die Hauptkomponenten sind: ExecutorService, ThreadPoolExecutor und verschiedene Implementierungen von BlockingQueue.
1. ExecutorService – Hochwertiges Interface
ExecutorService ist ein Interface, das Methoden zur asynchronen Ausführung von Aufgaben bereitstellt. Es verbirgt die Details der Thread-Verwaltung. Die gängigste Methode, eine Instanz zu erhalten, ist die Verwendung der Factory-Methoden der Klasse Executors:
ExecutorService executor = Executors.newFixedThreadPool(4); // Pool mit 4 Threadsexecutor.submit(() -> { System.out.println("Aufgabe wird in Thread ausgeführt: " + Thread.currentThread().getName());});executor.shutdown(); // Beendigung nach Ausführung aller AufgabenAllerdings haben die Factory-Methoden nur begrenzte Konfigurationsmöglichkeiten. Für eine Feinabstimmung wird ThreadPoolExecutor direkt verwendet.
2. ThreadPoolExecutor – Konfigurierbarer Thread-Pool
ThreadPoolExecutor ist der Kern des Frameworks. Er akzeptiert eine Aufgabenwarteschlange, eine Sättigungsstrategie, die Lebensdauer von Threads und andere Parameter. Der vollständige Konstruktor:
public ThreadPoolExecutor( int corePoolSize, // minimale Anzahl von Threads int maximumPoolSize, // maximale Anzahl von Threads long keepAliveTime, // Leerlaufzeit eines Threads vor der Entfernung TimeUnit unit, // Zeiteinheit BlockingQueue<Runnable> workQueue, // Aufgabenwarteschlange RejectedExecutionHandler handler // Strategie bei Überlauf);Beispiel für die Erstellung eines Pools mit einer benutzerdefinierten Warteschlange:
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(100);ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, 4, 30, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.AbortPolicy() // Standard – Auslösen einer Exception);executor.execute(() -> { // schwere Aufgabe});executor.shutdown();3. BlockingQueue – Grundlage der Aufgabenwarteschlange
Der Typ der Warteschlange bestimmt, wie Aufgaben gespeichert und an Threads übergeben werden. Die gängigsten Implementierungen:
LinkedBlockingQueue– unbegrenzte Warteschlange basierend auf einer verketteten Liste. Wenn keine Kapazität angegeben wird, kann dies zu Speicherüberlauf führen.ArrayBlockingQueue– begrenzte Warteschlange basierend auf einem Array. Ermöglicht die Kontrolle über die Anzahl wartender Aufgaben.SynchronousQueue– Warteschlange mit null Kapazität. Jede Aufgabe wird direkt an einen Thread übergeben, es gibt kein Warten in der Warteschlange.PriorityBlockingQueue– Warteschlange mit Prioritäten. Aufgaben werden nach Priorität sortiert (implementierenComparable).
Beispiel mit einer begrenzten Warteschlange und einer Ablehnungsstrategie:
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(10);ThreadPoolExecutor executor = new ThreadPoolExecutor( 2, 5, 10, TimeUnit.SECONDS, queue, new ThreadPoolExecutor.CallerRunsPolicy() // Aufgabe wird im aufrufenden Thread ausgeführt);for (int i = 0; i < 20; i++) { executor.submit(() -> { Thread.sleep(100); return Math.random(); });}executor.shutdown();4. Sättigungsstrategien (RejectedExecutionHandler)
Wenn die Warteschlange üb
Auch in der Bibliothek
Aufgabenplaner in Go (Golang): cron, gocron und robfig/cron
QR-Code-Generator in Rust: Erstellung und Integration in Webanwendungen
Aufgabenwarteschlangen in Node.js: Bull, Bee-Queue und Kue
ORM für Rust: Vergleich von Diesel, SeaORM, SQLx und Codebeispiele
Redis-Client für Swift (iOS/macOS) – RediStack