Java Thread Pool und ThreadPoolExecutor Anleitung
Einführung in Java Thread Pool
Java-Thread-Pool verwaltet den Pool von Worker-Threads. Er enthält eine Warteschlange, in der Aufgaben auf ihre Ausführung warten. Wir können ThreadPoolExecutor verwenden, um in Java einen Thread-Pool zu erstellen. Java-Thread-Pool verwaltet die Sammlung von Runnable-Threads. Die Worker-Threads führen Runnable-Threads aus der Warteschlange aus. java.util.concurrent.Executors bieten Fabrik- und Unterstützungsmethoden für das java.util.concurrent.Executor-Interface zur Erstellung des Thread-Pools in Java. Executors ist eine Hilfsklasse, die auch nützliche Methoden zur Arbeit mit ExecutorService, ScheduledExecutorService, ThreadFactory und Callable-Klassen über verschiedene Fabrikmethoden bietet. In dieser Java Thread Pool und ThreadPoolExecutor Anleitung lernen Sie alles Wichtige.
Lassen Sie uns ein einfaches Programm schreiben, um seine Arbeitsweise zu erklären. Zuerst benötigen wir eine Runnable-Klasse, genannt WorkerThread.java
WorkerThread-Klasse
package com.journaldev.threadpool;
public class WorkerThread implements Runnable {
private String command;
public WorkerThread(String s){
this.command=s;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+" Start. Befehl = "+command);
processCommand();
System.out.println(Thread.currentThread().getName()+" Ende.");
}
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString(){
return this.command;
}
}
ExecutorService-Beispiel
Hier ist die Testprogrammklasse SimpleThreadPool.java, in der wir einen festen Thread-Pool aus dem Executors-Framework erstellen.
package com.journaldev.threadpool;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SimpleThreadPool {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executor.execute(worker);
}
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("Alle Threads beendet");
}
}
In dem oben genannten Programm erstellen wir einen festen Thread-Pool mit 5 Worker-Threads. Dann übergeben wir 10 Jobs an diesen Pool. Da die Poolgröße 5 beträgt, beginnt er mit der Bearbeitung von 5 Jobs und die anderen Jobs befinden sich im Wartezustand. Sobald einer der Jobs abgeschlossen ist, wird ein weiterer Job aus der Warteschlange von einem Worker-Thread übernommen und ausgeführt. Hier ist die Ausgabe des obigen Programms.
pool-1-thread-2 Start. Befehl = 1
...
Alle Threads beendet
ThreadPoolExecutor-Beispiel
Die Executors-Klasse bietet eine einfache Implementierung des ExecutorService mithilfe des ThreadPoolExecutors, aber ThreadPoolExecutor bietet viel mehr Funktionen als das. Wir können die Anzahl der Threads angeben, die aktiv sein werden, wenn wir eine Instanz von ThreadPoolExecutor erstellen, und wir können die Größe des Thread-Pools begrenzen und unsere eigene Implementierung von RejectedExecutionHandler erstellen, um die Jobs zu verwalten, die nicht in die Worker-Warteschlange passen. Hier ist unsere benutzerdefinierte Implementierung des RejectedExecutionHandler-Interfaces.
package com.journaldev.threadpool;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
public class RejectedExecutionHandlerImpl implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(r.toString() + " wird abgelehnt");
}
}
Überwachung des ThreadPoolExecutors
ThreadPoolExecutor bietet mehrere Methoden, mit denen wir den aktuellen Zustand des Executors, die Poolgröße, die Anzahl aktiver Threads und die Aufgabenanzahl herausfinden können. Ich habe also einen Überwachungsthread, der die Informationen des Executors in bestimmten Zeitabständen ausgibt.
package com.journaldev.threadpool;
import java.util.concurrent.ThreadPoolExecutor;
public class MyMonitorThread implements Runnable {
private ThreadPoolExecutor executor;
private int seconds;
private boolean run=true;
public MyMonitorThread(ThreadPoolExecutor executor, int delay) {
this.executor = executor;
this.seconds = delay;
}
public void shutdown() {
this.run = false;
}
@Override
public void run() {
while(run) {
System.out.println(
String.format("[monitor] [%d/%d] Aktiv: %d, Abgeschlossen: %d, Aufgabe: %d, isShutdown: %s, isTerminated: %s",
this.executor.getPoolSize(),
this.executor.getCorePoolSize(),
this.executor.getActiveCount(),
this.executor.getCompletedTaskCount(),
this.executor.getTaskCount(),
this.executor.isShutdown(),
this.executor.isTerminated()));
try {
Thread.sleep(seconds * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Beispiel für die Implementierung des ThreadPoolExecutors
Hier ist ein Beispiel für die Implementierung eines Thread-Pools mit ThreadPoolExecutor.
package com.journaldev.threadpool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class WorkerPool {
public static void main(String args[]) throws InterruptedException {
// RejectedExecutionHandler-Implementierung
RejectedExecutionHandlerImpl rejectionHandler = new RejectedExecutionHandlerImpl();
// Holen der ThreadFactory-Implementierung zur Verwendung
ThreadFactory threadFactory = Executors.defaultThreadFactory();
// Erstellung des ThreadPoolExecutor
ThreadPoolExecutor executorPool = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS, new ArrayBlockingQueue(2), threadFactory, rejectionHandler);
// Start des Monitor-Threads
MyMonitorThread monitor = new MyMonitorThread(executorPool, 3);
Thread monitorThread = new Thread(monitor);
monitorThread.start();
// Aufträge an den Thread-Pool senden
for(int i=0; i<10; i++) {
executorPool.execute(new WorkerThread("cmd" + i));
}
Thread.sleep(30000);
// Schließen des Pools
executorPool.shutdown();
// Schließen des Monitor-Threads
Thread.sleep(5000);
monitor.shutdown();
}
}
Beachten Sie, dass bei der Initialisierung des ThreadPoolExecutors die Anfangsgröße des Pools auf 2, die maximale Poolgröße auf 4 und die Größe der Warteschlange auf 2 festgelegt wird. Wenn es also 4 laufende Aufgaben gibt und weitere Aufgaben eingereicht werden, kann die Warteschlange nur 2 davon aufnehmen und der Rest wird vom RejectedExecutionHandlerImpl behandelt. Hier ist die Ausgabe des oben genannten Programms, die die obige Aussage bestätigt.
pool-1-thread-1 Start. Command = cmd0
...
[monitor] [0/2] Aktiv: 0, Abgeschlossen: 6, Aufgabe: 6, isShutdown: true, isTerminated: true
Beachten Sie die Änderung in der Anzahl der aktiven, abgeschlossenen und insgesamt abgeschlossenen Aufgaben des Executors. Wir können die Methode shutdown() aufrufen, um die Ausführung aller eingereichten Aufgaben zu beenden und den Thread-Pool zu schließen. Wenn Sie eine Aufgabe verzögert oder regelmäßig ausführen lassen möchten, können Sie die Klasse ScheduledThreadPoolExecutor verwenden. Lesen Sie mehr darüber im Java Schedule Thread Pool Executor.
Ausgabe-Analyse und Schlussfolgerung
Die Ausgabe bestätigt, dass es fünf Threads im Pool gibt, die von „pool-1-thread-1“ bis „pool-1-thread-5“ benannt sind und die eingereichten Aufgaben im Pool ausführen.
Die Ausgabe des oben genannten Programms zeigt die Funktionsweise des Thread-Pools. Der Pool verwaltet die eingereichten Aufgaben effizient und verteilt die Arbeitslast unter den verfügbaren Threads. Dies ist aus der Reihenfolge und dem Abschluss der Aufgaben ersichtlich, wie in der Ausgabe gezeigt.
Erweiterte Funktionen des ThreadPoolExecutors
ThreadPoolExecutor bietet weitaus mehr Funktionen als einfaches Thread-Pooling. Wir können die Anzahl der Threads festlegen, die aktiv sein werden, wenn wir eine Instanz von ThreadPoolExecutor erstellen, die Größe des Thread-Pools begrenzen und unsere eigene RejectedExecutionHandler-Implementierung erstellen, um Aufgaben zu verwalten, die nicht in die Warteschlange der Arbeiter passen.
Hier ist die Ausgabe des oben genannten Programms, die die obige Aussage bestätigt:
cmd6 wird abgelehnt
...
[monitor] [2/2] Aktiv: 0, Abgeschlossen: 6, Aufgabe: 6, isShutdown: false, isTerminated: false
Dieser Abschnitt zeigt die Anpassung und Kontrolle, die wir über das Thread-Pooling-Verhalten in Java haben, insbesondere mit dem ThreadPoolExecutor. Die Möglichkeit, Aufgaben abzulehnen und den Zustand des Thread-Pools zu überwachen, ist für den Aufbau robuster paralleler Anwendungen unerlässlich.
Schlussfolgerung
Diese Java Thread Pool und ThreadPoolExecutor Anleitung bot einen detaillierten Einblick in den ThreadPoolExecutor von Java und wie er verwendet werden kann, um Threads in einer Multi-Thread-Umgebung effizient zu verwalten. Die gegebenen Beispiele veranschaulichen die Funktionalität und Flexibilität des ThreadPoolExecutors, was ihn zu einem mächtigen Werkzeug für Java-Entwickler macht.
Für weitere Erkundungen und detaillierte Informationen werden die Leser ermutigt, sich in die Java-Dokumentation zu vertiefen und die Funktionen des Thread-Pools zu experimentieren, um sie an ihre spezifischen Bedürfnisse anzupassen.