2. Einführung
Die Multithreading -Technologie löst hauptsächlich das Problem der Ausführung mehrerer Threads in einer Prozessoreinheit. Es kann die Leerlaufzeit der Prozessoreinheit erheblich verkürzen und die Durchsatzfähigkeit der Prozessoreinheit erhöhen. Der Overhead der häufigen Fadenerstellung ist jedoch sehr groß. Wie Sie diesen Teil des Overheads reduzieren, müssen Sie in Betracht ziehen, einen Thread -Pool zu verwenden. Ein Threadpool ist ein Threadbehälter, der nur eine Nennzahl von Threads gleichzeitig ausführt. Der Thread -Pool wird verwendet, um diese Nennzahl von Threads zu verwalten.
3. Klassenstrukturdiagramm mit Threadpool
Unter ihnen ist die wichtigste für uns die ThreadPoolexecutor -Klasse.
4.. So erstellen Sie einen Thread -Pool
Wir haben im Allgemeinen die folgenden Methoden, um Threadpools zu erstellen:
1. Verwenden Sie die Fabrikklasse der Executoren
Die Executoren bieten hauptsächlich die folgenden Methoden zum Erstellen von Threadpools:
Schauen wir uns das folgende Nutzungsbeispiel an:
1) NeufixedThreadpool (fester Threadpool)
public class cipeadpool {public static void main (String [] args) {ExecutorServicepool = Executors. } pool.shutdown (); }} public class myThread erweitert Thread {@Override public void run () {System.out.println (Thread.currentThread (). getName () + "Ausführung ..."); }}Die Testergebnisse sind wie folgt:
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-2 wird ausgeführt. . .
Pool-1-Thread-3 wird ausgeführt. . .
Pool-1-Thread-2 wird ausgeführt. . .
Pool-1-Thread-3 wird ausgeführt. . .
Pool-1-Thread-2 wird ausgeführt. . .
Pool-1-Thread-2 wird ausgeführt. . .
Pool-1-Thread-3 wird ausgeführt. . .
Pool-1-Thread-5 wird ausgeführt. . .
Pool-1-Thread-4 wird ausgeführt. . .
Threadpool mit fester Größe: Erstellen Sie jedes Mal, wenn eine Aufgabe eingereicht wird, bis der Thread die maximale Größe des Threadpools erreicht. Die Größe des Fadenpools bleibt unverändert, sobald er seinen Maximalwert erreicht. Wenn ein Thread aufgrund einer Ausführungsausnahme endet, fügt der Thread -Pool einen neuen Thread hinzu.
2) NewsinglethreadExecutor (Einzel -Thread -Pool)
öffentliche Klasse SingLethreadpool {public static void main (String [] args) {ExecutorServicepool = Executors. } pool.shutdown (); }}Die Testergebnisse sind wie folgt:
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
Einer-Thread-Threadpool: Dieser Thread-Pool hat nur einen Thread, was bedeutet, dass eine serielle Einführung aller Aufgaben mit einem Thread-Thread. Wenn dieser einzigartige Faden aufgrund der Ausnahme endet, gibt es einen neuen Thread, der ihn ersetzt. Dieser Thread -Pool stellt sicher, dass die Ausführungsreihenfolge aller Aufgaben in der Reihenfolge der Aufgabenübermittlung ausgeführt wird.
3) NewsschonedThreadpool
Public Class Scheduled threadpool {public static void main (String [] args) {pleduledexecutorService pool = Executors.NewScheduledThreadpool (6); für (int i = 0; i <10000; i ++) {pool.submit (new myThread ()); } pool.Schedule (New MyThread (), 1000, TimeUnit.Milliseconds); Pool.Schedule (New MyThread (), 1000, TimeUnit.Milliseconds); pool.shutdown (); }}Die Testergebnisse sind wie folgt:
Pool-1-Thread-1 wird ausgeführt. . .
Pool-1-Thread-6 wird ausgeführt. . .
Pool-1-Thread-5 wird ausgeführt. . .
Pool-1-Thread-4 wird ausgeführt. . .
Pool-1-Thread-2 wird ausgeführt. . .
Pool-1-Thread-3 wird ausgeführt. . .
Pool-1-Thread-4 wird ausgeführt. . .
Pool-1-Thread-5 wird ausgeführt. . .
Pool-1-Thread-6 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
……………………………………………………………………………………………………………………………………………………………………………………………………………. …………………………………………………………………………………………………………………………………………………………………………………………………………….
Pool-1-Thread-4 wird ausgeführt. . .
Pool-1-Thread-1 wird ausgeführt. . .
Die letzten beiden Threads des Testergebnisses beginnen erst nach Verzögerung von 1s auszuführen. Dieser Thread -Pool unterstützt die Anforderungen an das Timing und die regelmäßige Ausführung von Aufgaben
4) NewcachedThreadpool (zwischengespeicherbarer Fadenpool)
öffentliche Klasse CachedThreadpool {public static void main (String [] args) {ExecutorService Pool = Executors.NewCachedThreadpool (); für (int i = 0; i <100; i ++) {pool.submit (new myThread ()); } pool.shutdown (); }}Die Testergebnisse sind wie folgt:
Pool-1-Thread-5 wird ausgeführt. . .
Pool-1-Thread-7 wird ausgeführt. . .
Pool-1-Thread-5 wird ausgeführt. . .
Pool-1-Thread-16 ist in der Ausführung. . .
Pool-1-Thread-17 wird ausgeführt. . .
Pool-1-Thread-16 ist in der Ausführung. . .
Pool-1-Thread-5 wird ausgeführt. . .
Pool-1-Thread-7 wird ausgeführt. . .
Pool-1-Thread-16 ist in der Ausführung. . .
Pool-1-Thread-18 wird ausgeführt. . .
Pool-1-Thread-10 wird ausgeführt. . .
Zwischengespeicherbarer Threadpool: Wenn die Größe des Thread -Pools den zum Verarbeiten der Aufgabe erforderlichen Thread überschreitet, werden einige Leerlauf -Threads (keine Aufgabenausführung in 60 Sekunden) recycelt. Wenn die Anzahl der Aufgaben zunimmt, kann dieser Thread -Pool intelligent neue Threads hinzufügen, um die Aufgabe zu verarbeiten. Dieser Thread -Pool begrenzt nicht die Gewindepoolgröße, die vollständig von der maximalen Gewindegröße abhängt, die das Betriebssystem (oder JVM) erstellen kann.
Der Beamte schlägt vor, dass Programmierer die bequemeren Fabrikmethoden ausführender verwenden. NewcachedThreadpool () (unbegrenzter Thread-Pool, mit dem automatische Thread-Recycling durchgeführt werden kann), Executors.NewFixed threadpool (int) (Int) (fester Größe des Thread-Size-Threads). Diese Threadpools werden standardmäßig für die meisten Nutzungsszenarien vordefiniert.
2. Erben Sie die ThreadPoolexecutor -Klasse und kopieren Sie die Konstruktormethode der übergeordneten Klasse.
Lassen Sie uns vor Einführung dieser Methode die vorherigen zugrunde liegenden Codes zum Erstellen von Threadpools analysieren?
public class Executors {public static ExecutorService Newfixed threadpool (int nthreads) {return neuer threadpoolexecutor (Nthreads, Nthreads, 0L, TimeUnit.Milliseconds, New LinkedBlockingQueue <Runnable> ()); } public static ExecutorService NewsingLethreadExecutor () {Neue endgültige endgültige AuslöserexecutorService zurückgeben (neuer ThreadPoolexecutor (1, 1, 0L, TimeUnit.Milliseconds, neu LinkedBlockingCocking <Runnable> ()); }}Aus dem zugrunde liegenden Code der Fabrikklasse der Executors können wir feststellen, dass die von der Fabrikklasse bereitgestellten Methoden zum Erstellen von Threadpools tatsächlich durch Erstellen von ThreadPoolexecutor implementiert werden. Der ThreadPoolexecutor -Konstruktor -Methodencode lautet wie folgt:
public threadpoolexecutor (int corepoolsize, int maximumpoolsize, long keepalivetime, timeunit unit, blockingqueue <Runnable> WorkQueue, ThreadFactory Factory, RejejecedExecutionHandler Handler) {if (corepoolsesize <0 || maximalSize <= 0 || MAXIMAUMSICELSIZEISISCHE <0,0 || maximalSize. IllegalArgumentException (); if (WorkQueue == null || threadFactory == null || Handler == null) neue nullpointerexception () werfen; this.corepoolsize = corepoolsize; this.maxImumpoolSize = maximumpoolSize; this.workQueue = WorkQueue; this.keepalivetime = unit.tonanos (keepalivetime); this.threadfactory = threadFactory; this.handler = Handler; }Sprechen wir dann über die ThreadPoolexecutor -Konstruktor -Methode. Bei dieser Konstruktionsmethode gibt es hauptsächlich die folgenden Parameter:
CorePoolSize-Die Anzahl der im Pool gespeicherten Threads, einschließlich kostenloser Threads.
MaximumpoolSize - Die maximale Anzahl der im Pool zulässigen Threads.
KeepAlivetime-Wenn die Anzahl der Threads größer ist als Corepoolsize, ist dies die längste Zeit für den Leerlauf-Thread, um auf eine neue Aufgabe zu warten.
Einheit- Keepalivetime Parameter-Zeiteinheit.
WorkQueue-Die Warteschlange führte zur Voraussetzung auf Aufgaben. Diese Warteschlange verwaltet nur von der Ausführungsmethode eingereichte laufbare Aufgaben.
ThreadFactory-Die Fabrik, die von Testamentszeiten verwendet werden, um neue Threads zu erstellen.
Handler-Der Handler, der bei der Ausführung verwendet wird, wird aufgrund des Thread-Bereichs und der Warteschlangenkapazität blockiert.
Lassen Sie uns als nächstes über die Beziehung zwischen diesen Parametern sprechen. Wenn der Thread -Pool gerade erstellt wird, befinden sich im Thread -Pool keine Threads (beachten Sie, dass nicht eine bestimmte Anzahl von Threads erstellt wird, sobald der Thread -Pool erstellt wird). Wenn die Methode execute () aufgerufen wird, um eine Aufgabe hinzuzufügen, fällt der Thread -Pool das folgende Urteil:
1) Wenn die Anzahl der derzeit ausgeführten Threads weniger als Corepoolsize ist, erstellen Sie sofort einen neuen Thread, um diese Aufgabe auszuführen.
2) Wenn die Anzahl der derzeit ausgeführten Threads größer oder gleich Corepoolsize ist, wird diese Aufgabe in die Warteschlange gestellt.
3) Wenn die Thread -Pool -Warteschlange voll ist, die Anzahl der laufenden Threads jedoch weniger als Maximumphoolsize ist, wird weiterhin ein neuer Thread erstellt, um diese Aufgabe auszuführen.
4) Wenn die Warteschlange voll ist und die Anzahl der derzeit laufenden Threads größer oder gleich Maximumphoolsize ist, wird der Thread -Pool die aktuelle Aufgabe basierend auf der Ablehnungsrichtlinie verarbeitet.
5) Wenn eine Aufgabe ausgeführt wird, nimmt der Thread die nächste Aufgabe aus der Warteschlange aus, um auszuführen. Wenn in der Warteschlange keine Aufgabe auszuführen ist, ist der Thread im Leerlauf. Wenn die Überlebenszeit der Keepalivetime überschreitet, wird der Thread vom Thread -Pool recycelt (Hinweis: Recycling -Threads sind bedingt. Wenn die Anzahl der derzeit ausgeführten Threads größer ist als Corepoolsize, wird der Thread zerstört. Wenn es nicht größer ist als Corepoolsize, wird der Thread nicht zerstört werden. Die Anzahl der Themen wird nicht zerstört. Warum ist es nicht so, dass der Thread recycelt wird, sobald er untätig ist, sondern dass er warten muss, bis er Keepalivetime übertrifft, bevor der Faden recycelt wird? Der Grund ist sehr einfach: Weil die Schöpfung und Zerstörung von Fäden viel verbraucht und nicht häufig erstellt und zerstört werden kann. Nachdem Keepalivetime überschritten wird, wird festgestellt, dass dieser Thread tatsächlich nicht verwendet wird und er zerstört wird. In diesem Fall repräsentiert die Einheit die Zeiteinheit von Keepalivetime, und die Definition der Einheit lautet wie folgt:
public enum timeunit {nanosekunden {// keepalivetime in nanosekunden}, mikrosekunden {// keepalivetime in microseseconds}, Millisekunden KeepAlivetime in Stunden}, Tage {// Keepalivetime in Tagen}; Lassen Sie uns den folgenden Quellcode analysieren. Für die oben genannten Situationen sind die Quellcodes hauptsächlich folgende:
private boolean addifundercorepoolsize (runnable firstTask) {Thread t = null; Final Reentrantlock Mainlock = this.mainlock; Mainlock.lock (); try {if (poolSize <corepoolSize && runState == running) t = addthread (firstTask); } endlich {mainlock.unlock (); } if (t == null) return false; t.start (); zurückkehren; } Tatsächlich ist dieser Code sehr einfach. Es wird hauptsächlich beschrieben, dass, wenn der aktuelle Threadpool kleiner als Corepoolsize ist, ein neuer Thread erstellt wird, um die Aufgabe zu verarbeiten.
private boolean addifundermaximumpoolsize (runnable firstTask) {Thread t = null; Final Reentrantlock Mainlock = this.mainlock; Mainlock.lock (); try {if (poolSize <maximumpoolsize && runState == running) t = addthread (firstTask); } endlich {mainlock.unlock (); } if (t == null) return false; t.start (); zurückkehren; }Der obige Code beschreibt, dass, wenn die Anzahl der aktuellen Threadpools weniger als Maximumpoolsize ist, ein Thread erstellt wird, um die Aufgabe auszuführen.
5. Die Warteschlange des Threadpools
Es gibt 3 Arten von Thread -Pool -Warteschlangen:
Direktes Commit: Die Standardoption für Arbeitswarteschlangen ist Synchronousqueue, die Aufgaben direkt an Threads unterbreitet, ohne sie zu behalten. Wenn hier kein Thread zur Verfügung steht, um die Aufgabe sofort auszuführen, wird der Versuch, die Aufgabe anzustellen, fehlschlägen und so einen neuen Thread erstellen. Diese Richtlinie vermeidet Schlösser bei der Bearbeitung von Anforderungssätzen, die möglicherweise interne Abhängigkeiten haben. Direkte Einsendungen erfordern normalerweise unbegrenzter Maximumpoolsize, um nicht neu eingereichte Aufgaben abzulehnen. Diese Strategie ermöglicht unbegrenzte Fäden, die Möglichkeit des Wachstums zu haben, wenn Befehle kontinuierlich mit einem Durchschnitt eintreffen, den die Warteschlange verarbeiten kann.
Unbegrenzte Warteschlange: Die Verwendung einer unbegrenzten Warteschlange (z. B. Linked BlockingCocue ohne vordefinierte Kapazität) lässt neue Aufgaben in der Warteschlange warten, wenn alle Threads Corepoolsize besetzt sind. Auf diese Weise überschreitet der erstellte Thread Corepoolsize nicht. (Der Wert von MaximumpoolSize ist ungültig.) Wenn jede Aufgabe völlig unabhängig von anderen Aufgaben ist, dh die Aufgabenausführung hat sich nicht gegenseitig auswirkt, sie ist für unbegrenzte Warteschlangen geeignet. Zum Beispiel in einem Webseitenserver. Diese Warteschlange kann verwendet werden, um vorübergehende Burst -Anforderungen zu bearbeiten, und diese Strategie ermöglicht unbegrenzte Threads, die Möglichkeit des Wachstums zu haben, wenn Befehle kontinuierlich den Durchschnitt überschreiten, den die Warteschlange verarbeiten kann.
Begrenzte Warteschlange: Bei der Verwendung begrenzter Maximumpoolsize helfen begrenzte Warteschlangen (wie ArrayBlockingqueue) die Erschöpfung der Ressourcen, kann jedoch schwierig sein, sich anzupassen und zu steuern. Die Warteschlangengröße und die maximale Poolgröße müssen möglicherweise gegenseitig unterbrochen werden: Die Verwendung großer Warteschlangen und kleinen Pools kann die CPU -Nutzung, das Betriebssystemressourcen und den Überkopf des Kontextschaltungsschalters minimieren, kann jedoch zu einer manuellen Reduzierung des Durchsatzes führen. Wenn Aufgaben häufig blockiert werden (z. B. wenn es sich um E/A -Grenzen handelt), kann das System Zeit für mehr Threads planen, als Sie zulassen. Die Verwendung kleiner Warteschlangen erfordert normalerweise eine größere Poolgröße und hat eine höhere CPU -Verwendung, kann jedoch auf inakzeptable Planungsaufwand auftreten, was auch den Durchsatz verringern kann.
Lassen Sie uns über die Thread Pool -Warteschlange unten sprechen. Das Klassenstrukturdiagramm lautet wie folgt:
1) Synchronousqueue
Die Warteschlange entspricht der oben genannten direkten Einreichung. Erstens ist Synchronousqueue unbegrenzt, was bedeutet, dass seine Fähigkeit, Zahlen zu speichern, unbegrenzt ist. Aufgrund der Eigenschaften der Warteschlange selbst müssen Sie nach dem Hinzufügen von Elementen jedoch darauf warten, dass andere Threads sie wegnehmen, bevor Sie sie weiter hinzufügen können.
2) Linked Blockingqueue
Die Warteschlange entspricht der obigen unbegrenzten Warteschlange.
3) ArrayBlockingqueue
Die Warteschlange entspricht der oben genannten Warteschlange. ArrayBlockingQueue hat die folgenden 3 Konstruktoren:
public arrayBlockingQueue (int Kapazität) {this (Kapazität, falsch); } public arrayBlockingQueue (int Kapazität, boolean fair) {if (Kapazität <= 0) werfen neue illegalArgumentException (); this.items = (e []) neues Objekt [Kapazität]; lock = new Reentrantlock (fair); notempely = lock.newcondition (); Notfull = lock.newcondition (); } public arrayBlockingQueue (int Kapazität, Boolesche Messe, Sammlung <? Erweitert E> c) {this (Kapazität, fair); if (Kapazität <c.Size ()) werfen neue illegalArgumentException (); für (iterator <? erweitert e> it = C.Inator (); it.hasnext ();) add (it.Next ()); }Konzentrieren wir uns auf diese Messe. Fair repräsentiert die Wettbewerbsstrategie von Warteschlangenzugriffs -Threads. Wenn die Taskeinbereitungswarteschlangen der Aufgaben in die FIFO -Regeln entsprechen. Wenn falsch, können Sie die Warteschlange schneiden. Wenn beispielsweise viele Aufgaben anstehen, hat ein Thread die Aufgabe abgeschlossen und eine neue Aufgabe kommt. Wenn es falsch ist, muss diese Aufgabe in der Warteschlange nicht in der Warteschlange gestellt werden. Sie können die Warteschlange direkt schneiden und dann ausführen. Wie in der Abbildung unten gezeigt:
6. Strategie zur Ausführung von Threadpool -Ablehnung
Wenn die Anzahl der Threads den Maximalwert erreicht, werden die Aufgaben zu diesem Zeitpunkt immer noch erfolgen, und zu diesem Zeitpunkt muss ich mich weigern, die Aufgaben zu akzeptieren.
ThreadPoolexecutor ermöglicht die Anpassung von Ausführungsrichtlinien, wenn das Hinzufügen einer Aufgabe fehlschlägt. Sie können die Methode SetRejectedExecutionHandler () des Thread -Pools aufrufen und die vorhandene Richtlinie durch das angepasste AblehnungsexecutionHandler -Objekt ersetzen. Die Standardverarbeitungsstrategie, die von ThreadPoolexecutor bereitgestellt wird, besteht darin, gleichzeitig Ausnahmeinformationen zu verwerfen und zu werfen. ThreadPoolexecutor bietet 4 vorhandene Richtlinien, nämlich:
Threadpoolexecutor.abortPolicy: Zeigt an, dass die Aufgabe abgelehnt und eine Ausnahme ausgelöst wird. Der Quellcode lautet wie folgt:
public statische Klasse AbortPolicy Implements RejequeDexecutionHandler { /*** Erstellt ein <tt> abortpolicy < /tt>. * / public abortpolicy () {} / *** löst immer AblehnungsexecutionException aus. * @param r Die von der Runnable aufgeforderte Aufgabe, ausgeführt zu werden */ public void rejectedExecution (runnable r, threadpoolexecutor e) {throw New rejecneexecutionException (); // Ausnahme werfen}}Threadpoolexecutor.discardpolicy: Dies bedeutet, dass die Aufgabe abgelehnt wird, aber keine Aktionen erfolgen. Der Quellcode lautet wie folgt:
public statische Klasse DistributPolicy -Geräte AblehnungsexecutionHandler { /*** Erstellt eine <tt> DevardPolicy < /tt>. * / public DecardPolicy () {} / *** tut nichts, was den Einfluss der Verringerung der Aufgabe hat r. * @param r Die von der Ausführung von ausgeführte Aufgabe.ThreadPoolexecutor.Callerrunspolicy: Gibt an, dass die Aufgabe abgelehnt und die Aufgabe direkt im Anrufer -Thread ausgeführt wird. Der Quellcode lautet wie folgt:
public statische Klasse Callerrunspolicy Implements RejequeDExecutionHandler { /*** Erstellt ein <tt> callerrunspolicy < /tt>. * / public Callerrunspolicy () {} / ** * führt die Aufgabe R im Thread des Anrufers aus, es sei denn, der Testamentsvollstrecker * wurde geschlossen. In diesem Fall wird die Aufgabe verworfen. * @param r Die für die Ausführung von ausgeführte Aufgabe * @Param E Der ausführende Ausführende, der versucht, diese Aufgabe auszuführen // Aufgaben direkt ausführen}}}Threadpoolexecutor.discardoldestpolicy: Dies bedeutet, dass die erste Aufgabe in der Task -Warteschlange zuerst verworfen wird, und dann wird die Aufgabe der Warteschlange hinzugefügt. Der Quellcode lautet wie folgt:
öffentliche statische Klasse DevardoldestPolicy Implements RejequeDExecutionHandler { /*** Erstellt ein <tt> verunstresstes Jahr < /tt> für den angegebenen Testamentsvollstrecker. */ public devardoldestpolicy () {} public void rejecdexecution (runnable r, threadpoolexecutor e) {if (! e.isshutdown ()) {e.getQueue (). POLL (); // die erste Aufgabe in der Warteschlange E.execute (R) abwerfen; // eine neue Aufgabe ausführen}}}Wenn Aufgaben kontinuierlich eintreffen, wird eine Aufgabe aus der Warteschlange befragt und dann eine neue Aufgabe ausgeführt.
Zusammenfassen
Das obige ist eine detaillierte Erklärung des vom Herausgebers eingeführten Thread -Pools des JDK. Ich hoffe, es wird für alle hilfreich sein. Wenn Sie Fragen haben, hinterlassen Sie mir bitte eine Nachricht und der Editor wird allen rechtzeitig antworten. Vielen Dank für Ihre Unterstützung auf der Wulin.com -Website!