Technischer Hintergrund des Threadpools
Bei objektorientierter Programmierung ist das Erstellen und Zerstören von Objekten zeitaufwändig, da das Erstellen eines Objekts Speicherressourcen oder andere mehr Ressourcen erfordert. Dies ist in Java umso mehr, wo die virtuelle Maschine versucht, jedes Objekt so zu verfolgen, dass sie nach der Zerstörung des Objekts Müll erfasst werden kann.
Eine Möglichkeit, die Effizienz von Serviceprogrammen zu verbessern, besteht daher darin, die Häufigkeit der Erstellung und Zerstörung von Objekten zu minimieren, insbesondere einige sehr ressourcenkonsumierende Objekte, die die Erstellung und Zerstörung erstellen. Die Verwendung vorhandener Objekte zum Servieren ist ein wichtiges Problem, das gelöst werden muss. In der Tat ist dies der Grund, warum einige "Pooling -Ressourcen" -Technologien hergestellt werden.
Beispielsweise sind viele gängige Komponenten, die üblicherweise in Android zu sehen sind, im Allgemeinen untrennbar mit dem Konzept des "Pools" untrennbar untrennbar mit verschiedenen Bildladebibliotheken und Netzwerkanforderungsbibliotheken verbunden. Auch wenn MeaAsge in Androids Messaging -Mechanismus meaaSge.obtain () verwendet, ist es das Objekt im verwendeten MeaAsge -Pool, daher ist dieses Konzept sehr wichtig. Die in diesem Artikel eingeführte Thread -Pooling -Technologie entspricht dieser Idee ebenfalls.
Vorteile des Threadpools:
1. Verwenden Sie Threads im Thread -Pool, um den Leistungsaufwand zu verringern, der durch die Erstellung und Zerstörung von Objekten verursacht wird.
2. Es kann die maximale Parallelitätszahl von Threads effektiv steuern, die Nutzung der Systemressourcen verbessern und übermäßige Ressourcenwettbewerbe vermeiden und eine Blockade vermeiden.
3.. Fähigkeit, eine einfache Verwaltung mehrerer Threads durchzuführen, wodurch die Verwendung von Threads einfach und effizient gestaltet wird.
Thread Pool Framework Executor
Der Thread -Pool in Java wird über das Ausführungsrahmen implementiert. Das Framework des Executors umfasst Klassen: Executor, Executors, Executorservice, ThreadPoolexecutor, Callable und Future, Futuretask -Nutzung usw.
Ausführender: Es gibt nur eine Methode für alle Thread -Pool -Schnittstellen.
public interface Executor {void execute (runnable command); }ExecutorService: Das Hinzufügen des Verhaltens von Executor ist die direkteste Schnittstelle der Ausführungs -Implementierungsklasse.
Executors: Bietet eine Reihe von Werksmethoden zum Erstellen des Thread -Pools, und die zurückgegebenen Threadpools implementieren alle die ExecutorService -Schnittstelle.
ThreadPoolexecutor: Die spezifische Implementierungsklasse von Threadpools. Die verschiedenen im Allgemeinen verwendeten Threadpools werden basierend auf dieser Klasse implementiert. Die Konstruktionsmethode lautet wie folgt:
public threadpoolexecutor (int corepoolsize, int maximumpoolsize, long keepalivetime, Zeiteinheit, Blockingqueue <Runnable> WorkQueue) {this (corepoolSize, maximumpoolsize, keepalivetime, uneinheit, arbeitsqueue, ausführende.CorePoolSize: Die Anzahl der Kernthreads im Thread -Pool und die Anzahl der im Thread -Pool ausgeführten Threads überschreiten niemals Corepoolsize und können standardmäßig überleben. Sie können die ZulassungscoretheadTimeout auf True einstellen, die Anzahl der Kernfäden beträgt 0, und Keepalivetime steuert die Zeitüberschreitungszeit aller Threads.
MaximumpoolSize: Die maximale Anzahl der vom Thread -Pool zugelassenen Threads;
Keepalivetime: Bezieht sich auf die Zeitüberschreitungszeit, in der der Leerlauffaden endet;
Einheit: ist eine Aufzählung, die die Einheit der Keepalivetime darstellt;
WorkQueue: Repräsentiert die Blockingqueue <Runnable -Warteschlange, die die Aufgabe speichert.
Blockingqueue: Blockingqueue ist ein Werkzeug, das hauptsächlich zur Steuerung der Synchronisation unter java.util.concurrent verwendet wird. Wenn der Blockqueue leer ist, wird der Betrieb von etwas aus dem Blockingqueue blockiert und erst erweckt, wenn das Blockingqueue in das Ding eintritt. Wenn der Blockingqueue voll ist, wird jede Operation, die versucht, Dinge zu speichern, blockiert und erst erweckt, um den Betrieb fortzusetzen, bis der Blockierungsqueue Platz gibt. Blockierende Warteschlangen werden häufig in Szenarien von Produzenten und Verbrauchern verwendet. Hersteller sind Fäden, die Warteschlangen Elemente hinzufügen, und Verbraucher sind Fäden, die Elemente aus Warteschlangen entnehmen. Eine blockierende Warteschlange ist der Behälter, in dem der Hersteller Elemente speichert und der Verbraucher nur Elemente aus dem Behälter nimmt. Zu den spezifischen Implementierungsklassen gehören LinkedBlockingQueue, ArrayBlockingQuoed usw. Im Allgemeinen werden die internen Blockierungen und das Aufwachen durch Sperren und Zustand (Lernen und Verwendung von Anzeigeschlössern und Bedingungen) erreicht.
Der Arbeitsprozess des Thread -Pools lautet wie folgt:
Als der Thread -Pool zum ersten Mal erstellt wurde, gab es keinen Thread im Inneren. Die Task -Warteschlange wird als Parameter übergeben. Selbst wenn in der Warteschlange Aufgaben vorhanden sind, führt der Thread -Pool sie nicht sofort aus.
Wenn der Thread -Pool eine Aufgabe durch Aufrufen von EXECUTE () -Methoden hinzufügt, fällt der Thread -Pool das folgende Urteil:
Wenn die Anzahl der laufenden Threads geringer ist als Corepoolsize, erstellen Sie einen Thread, um diese Aufgabe sofort auszuführen.
Wenn die Anzahl der laufenden Threads größer oder gleich Corepoolsize ist, setzen Sie diese Aufgabe in die Warteschlange ein.
Wenn die Warteschlange zu diesem Zeitpunkt voll ist und die Anzahl der laufenden Threads weniger als Maximumpoolsize ist, müssen Sie dennoch einen Nicht-Kern-Thread erstellen, um die Aufgabe sofort auszuführen.
Wenn die Warteschlange voll ist und die Anzahl der laufenden Threads größer oder gleich Maximumphoolsize ist, wirft der Thread -Pool eine Ausnahme ab.
Wenn ein Thread eine Aufgabe erledigt, benötigt die nächste Aufgabe von der Warteschlange, um auszuführen.
Wenn ein Thread nichts zu tun hat und nach einer bestimmten Zeit (Keepalivetime) beurteilt der Thread -Pool, dass der Thread gestoppt wird, wenn die Anzahl der derzeit ausgeführten Threads größer ist als Corepoolsize. Nachdem alle Aufgaben des Fadenpools erledigt sind, schrumpfen er schließlich auf die Größe von Corepoolsize.
Erstellung und Verwendung von Fadenpools
Der Generierungs -Threadpool verwendet die statische Methode der Werkzeugklassen -Ausführende. Das Folgende sind mehrere gemeinsame Fadenbecken.
SinglethreadExecutor: Single -Hintergrund -Thread (die Pufferwarteschlange ist unbegrenzt)
public static ExecutorService NewsingLethreadExecutor () {Neue endgültige endgültige Ausführung von ThreadPoolexecutor (1, 1, 0L, TimeUnit.Milliseconds, neuer Linked -BlockingQueue <Runnable> ()); }Erstellen Sie einen einzelnen Threadpool. Dieser Thread -Pool hat nur einen Kern -Thread, der einem einzelnen Faden entspricht, der alle Aufgaben in der Serie ausführt. 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.
FixedThreadpool: Nur der Fadenpool von Kernfäden mit fester Größe (die Pufferwarteschlange ist unbegrenzt).
public static Executorservice Newfixed threadpool (int nthreads) {
Rückgabe neuer ThreadPoolexecutor (NThreads, Nthreads,
0L, TimeUnit.Millisekunden,,
neu Linked BlockingQueue <Runnable> ());
}
Erstellen Sie einen Threadpool mit fester Größe. Jedes Mal, wenn eine Aufgabe übermittelt wird, wird ein Thread erstellt, bis der Thread die maximale Größe des Threadpools erreicht. Die Gewindepoolgröße bleibt gleich, sobald sie ihren Maximalwert erreicht. Wenn ein Thread aufgrund einer Ausführungsausnahme endet, fügt der Thread -Pool einen neuen Thread hinzu.
CachedThreadpool: Unbegrenzter Thread Pool, kann automatisches Fadenrecycling durchführen.
public static ExecutorService NewCachedThreadpool () {Neue threadpoolexecutor zurückgeben (0, Integer.max_value, 60L, TimeUnit.seconds, New Synchronousqueue <Runnable> ()); }Wenn die Größe des Thread -Pools den Thread überschreitet, der zur Verarbeitung der Aufgabe erforderlich ist, werden einige der 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. Synchronousqueue ist eine blockierende Warteschlange mit Puffer von 1.
Geplanter Threadpool: Ein Kern -Thread -Pool mit festem Kern -Thread -Pool, unbegrenzte Größe. Dieser Thread -Pool unterstützt die Notwendigkeit, Aufgaben regelmäßig und regelmäßig auszuführen.
public static ExecutorService NewScheduledThreadpool (int corepoolsize) {Rückgabe neuer geplanter Threadpool (CorepoolSize, Integer }Erstellen Sie einen Thread -Pool, der regelmäßig Aufgaben ausführt. Bei Leerlauf wird der Nicht-Kern-Thread-Pool in der Zeit default_keepalivemillis recycelt.
Es gibt zwei am häufigsten verwendete Methoden zum Einreichen von Aufgaben in Threadpools:
ausführen:
ExecutorService.execute (runnable runbar);
Einreichen:
Futuretask Task = ExecutorService.Submit (runnable runnable);
Futuretask <t> task = ausführungsservice.submit (Runnable Runnable, T Ergebnisse);
Futuretask <t> task = ausführungsservice.submit (Callable <T> Callable);
Gleiches gilt für die Implementierung von Submit (Callable Callable) und das gleiche gilt für die Senden (Runnable Runnable).
public <T> Future <T> Subjekt (Callable <T> Task) {if (task == null) werfen neue nullpointerexception (); Futuretask <t> ftask = newtaskfor (Aufgabe); ausführen (ftask); ftask zurückgeben;}Es ist ersichtlich, dass Subjekt eine Aufgabe ist, die ein Ergebnis zurückgibt und ein Futuretask -Objekt zurückgibt, damit das Ergebnis über die Get () -Methode erhalten werden kann. Der endgültige Anruf zum Senden wird ebenfalls ausgeführt (runnable Runable). Senden Sie nur ein, was das Callable -Objekt in ein Futuretask -Objekt ausgeleitet oder ausgeleitet hat. Da Futuretask ein Runnable ist, kann es in Execute ausgeführt werden. Sehen Sie sich die Verwendung von Callable, Future, Futuretask verwendet, um wie Callable -Objekte und Runnable in Futuretask -Objekte eingekapselt sind.
Das Prinzip der Thread -Pool -Implementierung
Wenn Sie nur über die Verwendung von Threadpools sprechen, hat dieser Blog keinen großen Wert. Bestenfalls ist es nur ein Prozess, sich mit der API der Executor vertraut zu machen. Der Implementierungsprozess des Thread -Pools verwendet das synchronisierte Schlüsselwort nicht, sondern verwendet volatile, sperren und synchrone (blockierende) Warteschlangen, atomarische Klassen, Futuretask usw., da letztere eine bessere Leistung haben. Der Verständnisprozess kann die Idee der gleichzeitigen Kontrolle im Quellcode gut lernen.
Die Vorteile des zu Beginns erwähnten Thread -Pooling werden wie folgt zusammengefasst:
Wiederverwendung von Thread
Steuern Sie die maximale Anzahl von Gleichzeitigkeiten
Threads verwalten
1. Faden -Multiplexing -Prozess
Um das Prinzip des Thread -Multiplexing zu verstehen, sollten Sie zunächst den Lebenszyklus von Threads verstehen.
Während des Lebenszyklus eines Threads muss es fünf Staaten durchlaufen: neu, laufbar, laufend, blockiert und tot.
Thread erstellt einen neuen Thread durch neu. Dieser Vorgang soll einige Threadinformationen initialisieren, z. B. Threadname, ID, Gruppe, zu der der Thread gehört, usw., die als nur ein gewöhnliches Objekt angesehen werden können. Nach dem Aufrufen von Thread's Start () erstellt die Java Virtual Machine einen Methoden -Call -Stack und einen Programmzähler dafür, und gleichzeitig hat es zu True, und dann wird es eine Ausnahme geben, wenn die Startmethode aufgerufen wird.
Der Thread in diesem Zustand beginnt nicht zu laufen, bedeutet jedoch nur, dass der Thread ausgeführt wird. Wenn der Thread ausgeführt wird, hängt er vom Scheduler im JVM ab. Wenn der Faden die CPU erhält, wird die Run () -Methode aufgerufen. Nennen Sie Thread's Run () -Methode nicht selbst. Wechseln Sie dann zwischen der CPU-Zeitplanung zwischen rede-run-blockieren, bis die Run () -Methode endet oder andere Methoden den Thread anhalten und in den toten Zustand eingeben.
Daher sollte das Prinzip der Wiederverwendung von Threads darin bestehen, den Thread am Leben zu erhalten (bereit, laufend oder blockiert). Schauen wir uns anschließend an, wie ThreadPoolexecutor die Wiederverwendung von Threads implementiert.
Die Hauptarbeiterklasse in ThreadPoolexecutor steuert die Wiederverwendung von Thread. Schauen Sie sich den vereinfachten Code der Arbeiterklasse an, damit es leicht zu verstehen ist:
private endgültige Klasse Worker Implements Runnable {endgültiger Thread -Thread; Runnable FirstTask; Worker (Runnable FirstTask) {this.firsttask = firstTask; this.thread = GetThreadFactory (). null; while (task! = null || (task = getTask ())!Arbeiter ist ein Runnable und hat gleichzeitig einen Thread. Dieser Thread ist der zu öffnende Faden. Beim Erstellen eines neuen Arbeiterobjekts wird gleichzeitig ein neues Thread -Objekt erstellt, und der Arbeiter selbst wird als Parameter in Thread übergeben. Auf diese Weise läuft die Run () -Methode des Workers, wenn die Start () -Methode des Threads aufgerufen wird. Dann gibt es in Runworker () eine Weile Schleife, die das Runnable -Objekt immer wieder von Gettask () erhält und es nacheinander ausführt. Wie bekommt Gettask () das Runnable -Objekt?
Immer noch der vereinfachte Code:
private runnable gettask () {if (einige Sonderfälle) {return null; } Runnable r = WorkQueue.take (); return r;}Dieser Workqueue ist die Blockingqueue -Warteschlange, die Aufgaben beim Initialisieren von ThreadPoolexecutor speichert. Die Warteschlange speichert die auszuführenden Aufgaben. Da Blockingqueue eine blockierende Warteschlange ist, wird Blockingqueue.take () leer und tritt in den Wartezustand ein, bis ein neues Objekt in Blockingqueue hinzugefügt wird, um den blockierten Faden aufzuwecken. Im Allgemeinen endet im Allgemeinen die Run () -Fadenmethode nicht, sondern wird weiterhin laufbare Aufgaben von WorkQueue ausführen, die das Prinzip der Wiederverwendung von Threads erzielen.
2. steuern Sie die maximale Anzahl von Genehmigungen
Wann ist Runnable in WorkQueue gesteckt? Wann wird der Arbeiter erstellt? Wann wird der Thread in Worker genannt start (), um einen neuen Thread zu öffnen, um die Methode der Worker Run () auszuführen? Die obige Analyse zeigt, dass der Runworker () in Worker Aufgaben nacheinander seriell ausführt. Wie manifestiert sich also die Parallelität?
Es ist leicht zu glauben, dass Sie bei der Ausführung einige der oben genannten Aufgaben ausführen werden (Runnable Runnable). Mal sehen, wie es im Ausführen gemacht wird.
ausführen:
Vereinfachter Code
public void execute (runnable command) {if (command == null) werfen neue nullpointerexception (); int c = ctl.get (); // Aktuelle Anzahl der Threads <corepoolsize (WorkerCountOf (c) <corepoolse) {// Starten Sie direkt einen neuen Thread. if (addWorker (Befehl, wahr)) return; (!ADDWORKER:
Vereinfachter Code
Private boolean Addworker (Runnable FirstTask, boolean core) {int wc = WorkerCountOf (c); if (WC> = (Core? corepoolSize: maximumpoolSize)) {return false;} W = neuer Arbeiter (FirstTask); endgültiger Thread T = W.Thread; T.Start ();};};};};};};};Sehen wir uns nach dem Code die oben genannte Situation des Hinzufügens von Aufgaben während der Arbeitspool-Arbeit an:
* Wenn die Anzahl der laufenden Threads geringer ist als Corepoolsize, erstellen Sie einen Thread, um diese Aufgabe sofort auszuführen.
* Wenn die Anzahl der laufenden Threads größer oder gleich dem Corepoolsize ist, legen Sie diese Aufgabe in die Warteschlange ein.
* Wenn die Warteschlange zu diesem Zeitpunkt voll ist und die Anzahl der laufenden Threads weniger als Maximumpoolsize ist, müssen Sie dennoch einen Nicht-Kern-Thread erstellen, um die Aufgabe sofort auszuführen.
* Wenn die Warteschlange voll ist und die Anzahl der laufenden Threads größer oder gleich Maximumphoolsize ist, wirft der Thread -Pool eine Ausnahme ab.
Aus diesem Grund wird die Asynctask von Android parallel ausgeführt und überschreitet die maximale Anzahl von Aufgaben und wirft eine Ablehnung aus. Weitere Informationen finden Sie in der neuesten Version der Asynctask -Quellcode -Interpretation und der dunklen Seite von Asynctask
Wenn ein neuer Thread erfolgreich über Addworker erstellt wird, starten Sie () und verwenden Sie FirstTask als erste Aufgabe in Run () in diesem Arbeiter.
Obwohl die Aufgabe eines jeden Arbeiters die serielle Verarbeitung ist, werden sie parallel verarbeitet, wenn mehrere Arbeitnehmer erstellt werden, da er sich über einen Arbeitsqueue verfügt.
Daher wird die maximale Parallelitätszahl gemäß Corepoolsize und Maximumpoolsize gesteuert. Der allgemeine Prozess kann durch die folgende Abbildung dargestellt werden.
Die obige Erklärung und Bilder können gut verstanden werden.
Wenn Sie sich mit Android -Entwicklung befassen und mit den Prinzipien des Handlers vertraut sind, denken Sie vielleicht, dass dieses Bild ziemlich vertraut ist. Einige der Prozesse sind denen sehr ähnlich, die von Handler, Looper und MeaAsge verwendet werden. Handler.Send (Nachricht) entspricht der Ausführung (runnulble). Die in Looper gehaltene MeaAsge -Warteschlange entspricht Blockingqueue. Sie müssen diese Warteschlange jedoch durch Synchronisation beibehalten. Die Loop () -Funktion in Looper Loops, um MeaAsge von der MEAASGE -Warteschlange zu entnehmen, und die Runwork () in Worker wird kontinuierlich vom Blocking -Blocking -Blocking ausgelöst.
3. Verwalten Sie Threads
Über den Thread -Pool können wir die Wiederverwendung von Threads verwalten, die Parallelitätsnummer steuern und Prozesse zerstören. Die Wiederverwendung und Kontrolle der Threads wurde oben diskutiert, und der Thread -Management -Prozess wurde in sie durchsetzt, was leicht zu verstehen ist.
In ThreadPoolexecutor gibt es eine CTL -Atomicinder -Variable. Zwei Inhalte werden über diese Variable gespeichert:
Die Anzahl aller Threads. Jeder Thread befindet sich in einem Zustand, in dem die unteren 29 Bit von Fäden gespeichert und die höheren 3 Bit Runstate gespeichert werden. Unterschiedliche Werte werden durch Bitoperationen erhalten.
Private Final Atomicinteger ctl = neuer Atomicinder (ctlof (laufen, 0)); // Erhalten Sie den Zustand des Threads Private statische Int RunStateof (int c) {return c & ~ Kapazität;} // Erhalten Sie die Anzahl der Arbeitnehmer private statische statische Int Workountof (Int c) {return c). Kapazität;} // Begriff, ob der Thread private statische boolean isrunning (int c) {return c <stilldown;} ausführtHier wird der Thread -Pool -Shutdown -Vorgang hauptsächlich durch Abschalten und Stilldownnow () analysiert. Zunächst verfügt der Thread -Pool über fünf Zustände, um die Aufgabenzusatz und die Ausführung zu steuern. Die folgenden drei Haupttypen werden eingeführt:
Auslaufstatus: Der Thread -Pool wird normal ausgeführt und kann neue Aufgaben und Prozessaufgaben in der Warteschlange akzeptieren.
Herunterfahrenstatus: Es werden keine neuen Aufgaben akzeptiert, aber Aufgaben in der Warteschlange werden ausgeführt.
STOP -Status: Es werden keine neuen Aufgaben mehr akzeptiert, und die Aufgabe wird in der Warteschlange nicht verarbeitet. Diese Methode setzt RunState auf das Herunterfahren und beendet alle Leerlauffäden, während die noch nicht funktionierenden Threads nicht betroffen sind, sodass die Taskperson in der Warteschlange ausgeführt wird.
Die Shortdownnow -Methode setzt RunState an, um zu stoppen. Die Differenz zwischen der Abschaltmethode beendet diese Methode alle Threads, sodass die Aufgaben in der Warteschlange nicht ausgeführt werden.
Zusammenfassung: Durch die Analyse des ThreadPoolexecutor -Quellcodes haben wir ein allgemeines Verständnis des Erstellens von Threadpools, dem Hinzufügen von Aufgaben und der Ausführung von Threadpools. Wenn Sie mit diesen Prozessen vertraut sind, ist es einfacher, Threadpools zu verwenden.
Die aus IT erfasste Unterhaltungskontrolle und die Verwendung der Aufgabenverarbeitung von Produzentenverbrauchermodellen wird in Zukunft von großer Hilfe sein, um andere damit verbundene Probleme zu verstehen oder zu lösen. Zum Beispiel ist der Handler -Mechanismus in Android und die Messager -Warteschlange in Looper auch in Ordnung, um einen Blökqueue zu verwenden. Dies ist der Gewinn, den Quellcode zu lesen.
Das obige ist die Informationen, die den Java -Thread -Pool aussortieren. Wir werden in Zukunft weiterhin relevante Informationen hinzufügen. Vielen Dank für Ihre Unterstützung für diese Website!