Themen sind häufig an der Arbeit beteiligt. Zum Beispiel werden einige Aufgaben häufig an Threads zur asynchronen Ausführung übergeben. Oder das Serverprogramm legt für jede Anforderung eine separate Thread -Verarbeitungsaufgabe fest. Jenseits von Threads, wie beispielsweise die von uns verwendete Datenbankverbindung. Diese Schöpfung, Zerstörung oder Öffnung und Schließung haben einen großen Einfluss auf die Systemleistung. Daher wird die Verwendung von "Pool" hervorgehoben.
1. Warum verwenden Sie den Threadpool?
In der in Abschnitt 3.6.1 beschriebenen Implementierung wird jedem Kunden ein neuer Arbeiter -Thread zugewiesen. Wenn der Arbeiter -Thread mit dem Kunden kommuniziert, wird der Thread zerstört. Diese Implementierungsmethode hat die folgenden Mängel:
• Der Overhead von Servererstellung und -zerstörung (einschließlich Zeit- und Systemressourcen) ist sehr hoch. Dieser Artikel muss nicht erläutert werden, Sie können den "Thread -Erstellungsprozess" überprüfen. Zusätzlich zu den von der Maschine selbst geleisteten Arbeiten müssen wir auch instanziieren und beginnen, was alle die Belegung von Stapelressourcen erfordern.
• Zusätzlich zum Aufwand des Erstellens und Zerstörens von Threads konsumieren aktive Threads auch Systemressourcen. Dies sollte ein Verbrauch von Stapelressourcen sein. Es wird auch in Betracht gezogen, die Anzahl der Datenbankverbindungen zu erraten.
• Wenn die Anzahl der Threads festgelegt ist und jeder Thread einen langen Deklarationszyklus hat, ist auch relativ festgelegt. Unterschiedliche Betriebssysteme haben unterschiedliche Schaltzyklen, normalerweise etwa 20 ms. Der hier erwähnte Switch soll die CPU -Nutzungsrechte zwischen Threads unter der Planung von JVM und dem zugrunde liegenden Betriebssystem übertragen. Wenn häufig Threads erstellt und zerstört werden, werden Threads häufig umgeschaltet, da nach einer Zerstörung des Fadens das Recht zu verwenden muss, dem vorbereiteten Thread zu verwenden, damit der Thread eine Chance zum Ausführen erhalten kann. In diesem Fall folgt das Schalten zwischen Threads nicht dem festen Schaltzyklus des Systems, und der Overhead von Schaltwächen ist noch größer als der Overhead von Schöpfung und Zerstörung.
Relativ gesehen werden einige Threads mit Threadpools vorgefertigt, die Aufgaben ständig aus der Arbeitswarteschlange entfernen und dann die Aufgabe ausführen. Wenn der Worker -Thread eine Aufgabe erledigt, führt er weiterhin eine andere Aufgabe in der Arbeitswarteschlange aus. Die Vorteile sind wie folgt:
• Reduziert die Anzahl der Schöpfung und Zerstörung, und jeder Arbeiter -Thread kann ständig wiederverwendet werden und mehrere Aufgaben ausführen.
• Die Anzahl der Fäden im Gewindepool kann nach der Tragfähigkeit des Systems leicht eingestellt werden, um Systemabfälle aufgrund übermäßiger Systemressourcen zu verhindern.
2. Einfache Implementierung des Threadpools
Unten ist ein einfacher Thread -Pool, der von mir geschrieben wurde, der auch direkt aus dem Java Network -Programmierbuch entnommen wurde
Paket-Thread; importieren java.util.linkedList;/*** Implementierung des Thread-Pools, basierend auf der Länge, der maximalen Länge und der Warteschlangenlänge des regulären Threadpools können wir die Anzahl der Implementierungen erhöhen* @Author Han*/Public Class Mytheadpool erweitert ThreadGroup {// CPU-Nummer --- runnen. // ob private boolean geschlossen werden soll = false; // Queue Private LinkedList <Runnable> WorkQueue; // Thread Pool -ID private statische int threadpoolid; privat int threadid; public myThreadpool (int poolsize) {super ("mytheadpool."+threadpoolid); threadpoolid ++; setdaemon (wahr); WorkQueue = new LinkedList <Runnable> (); für (int i = 0; i <poolsize; i ++) {new WorkThread (). start (); }} // Hier können Sie in ConcurrentLinkedQueue umgehen, um die Effizienz der Verwendung synchronisierter öffentlicher synchronisierter void -Execute (Runnable Task) {if (isclosed) {neue IllegalStateException ("Verbindungspool wurde geschlossen ..."); } else {WorkQueue.add (Aufgabe); benachrichtigen(); }} Protected Synchronisierte runnable getTask () löscht InterruptedException {while (WorkQueue.size () == 0) {if (iscLosed) {return null; } Warten(); } return WorkQueue.removeFirst (); } public synchronisierte void close () {if (! iscLosed) {isclosed = true; WorkQueue.clear (); unterbrechen(); }} public void join () {synchronisiert (this) {iscLosed = true; notifyAll (); } Thread [] threads = neuer Thread [activeCount ()]; int count = aufzählende (Threads); für (int i = 0; i <count; i ++) {try {threads [i] .Join (); } catch (Ausnahme E) {}}} Klasse Workthread erweitert Thread {public WorkThread () {Super (mytheadpool.this, "WorkThread"+(ThreadID ++)); System.out.println ("erstellen ..."); } @Override public void run () {while (! IsInterrupted ()) {System.out.println ("run .."); Runnable task = null; Versuchen Sie {// Dies ist eine Blockierungsmethode Task = getTask (); } catch (Ausnahme e) {} if (task! = null) {task.run (); } else {break; }}}}}}Dieser Thread-Pool definiert hauptsächlich eine Arbeitswarteschlange und einige vorgezogene Fäden. Solange die Ausführungsmethode aufgerufen wird, können Sie Aufgaben an den Thread senden.
Wenn es keine Aufgabe gibt, blockiert der nachfolgende Thread in Gettask (), bis eine neue Aufgabe eingeht und geweckt wird.
Sowohl Join als auch Close können verwendet werden, um den Fadenpool zu schließen. Der Unterschied besteht darin, dass Join die Aufgaben in der Warteschlange erledigt, während Close sofort die Warteschlange löscht und alle Arbeiterfäden unterbricht. Der Interrupt () in close () entspricht dem Aufruf der jeweiligen Interrupt (), die untergeordnete Threads in der Threadgroup enthält. Wenn ein Faden wartet oder schläft, wird eine Interruptexception geworfen.
Die Testklasse lautet wie folgt:
public class testmythreadpool {public static void main (String [] args) löst unterbrochene Ausnahme aus {mytheadpool pool = new mytheadpool (3); für (int i = 0; i <10; i ++) {pool.execute (new Runnable () {@Override public void run () {try {thread.sleep (1000);} catch (interruptedException e) {} System.out.println ("funktionieren ..."); } pool.join (); //pool.close (); }}3. Der Thread -Pool, der von der JDK -Klassenbibliothek bereitgestellt wird
Java bietet eine gute Thread -Pool -Implementierung, die robuster und effizienter ist als unsere eigene Implementierung und auch leistungsstärkere Funktionen.
Das Klassendiagramm lautet wie folgt:
Die Senioren haben bereits eine gute Erklärung für diese Art von Threadpool gegeben. Jeder Java -Thread -Pool unter Baidu hat sehr detaillierte Beispiele und Tutorials geschrieben, daher werde ich sie hier nicht wiederholen.
4. Federinjektionsfadenpool
Bei Verwendung des Spring-Frameworks ist es sehr unpraktisch, in Multi-Thread-Anwendungen zu verwalten, wenn wir die von Java bereitgestellten Methoden verwenden, um einen Thread-Pool zu erstellen, und entspricht nicht unserer Vorstellung von der Verwendung von Feder. (Obwohl die Feder durch statische Methoden injiziert werden kann)
Tatsächlich bietet der Frühling selbst auch eine gute Implementierung von Threadpools. Diese Klasse heißt threadpooltaskexecutor.
Die Konfiguration im Frühjahr lautet wie folgt:
<bean id = "ExecutorService"> <Eigenschaft name = "corepoolSize" value = "$ {threadpool.corepoolsize}" /> <!-Mindestanzahl der Threads, die mit Thread Pool gepflegt werden-> <Property name = "keepaliveseconds" value = "$ {ThreadPool.KeepaliveSconds}" /> <!----" /> <! name = "maxpoolsize" value = "$ {threadpool.maxpoolsize}" /> <!-Maximale Anzahl von Threads, die vom Thread-Pool verwaltet werden-> <Eigenschaft name = "Queuecapacity" value = "$ {Threadpool.queuecapacity}" /> <!-Der vom Thread-Pool verwendete POMUE-POOL-> < /bean> < /ban>5. Hinweise zur Verwendung von Threadpools
•Deadlock
Jedes Multithread -Programm hat das Risiko einer Deadlockung. Der einfachste Fall ist, dass zwei Threads AB, A entsperrt, Sperre 1, Anforderungssperr 2, B Sperre 2 und Anforderungssperrung 1. (Diese Situation wird auch in der MySQL -Exklusivschloss angezeigt, und die Datenbank meldet direkt eine Fehlermeldung). Im Thread -Pool befindet sich ein weiterer Deadlock: Unter der Annahme, dass alle Worker -Threads im Thread -Pool während der Ausführung ihrer jeweiligen Aufgaben blockiert werden, warten sie auf das Ausführungsergebnis einer bestimmten Aufgabe A. Aufgabe A befindet sich in der Warteschlange und kann nicht ausgeführt werden, weil es keine müßigen Threads gibt. Auf diese Weise werden alle Ressourcen des Thread -Pools blockiert und Deadlocks werden generiert.
• Unzureichende Systemressourcen
Wenn die Anzahl der Threads im Thread -Pool sehr groß ist, verbrauchen diese Threads eine große Anzahl von Ressourcen, einschließlich Speicher- und anderer Systemressourcen, was die Systemleistung ernsthaft beeinträchtigt.
• Gleichzeitiger Fehler
Die Arbeitswarteschlange des Thread -Pools hängt von den Methoden Wartewait () und Benachrichtigung () ab, damit der Arbeiter Aufgaben rechtzeitig erfassen kann. Diese beiden Methoden sind jedoch schwer zu verwenden. Wenn der Code falsch ist, können Benachrichtigungen verloren gehen, wodurch der Worker -Thread untätig bleibt und die Aufgaben ignoriert, die in der Arbeitswarteschlange bearbeitet werden müssen. Weil es am besten ist, einige reifere Threadpools zu verwenden.
• Gewindeleckage
Ein schwerwiegendes Risiko der Verwendung von Gewindepools ist Gewindeleckage. Bei Thread -Pools mit fester Anzahl von Worker -Threads, wenn der Worker -Thread bei der Ausführung einer Aufgabe eine RunTimeException oder einen Fehler auslöst und diese Ausnahmen oder Fehler nicht erfasst werden, endet der Worker -Thread ungewöhnlich, wodurch der Thread -Pool dauerhaft einen Thread verliert. (Das ist so interessant)
Eine andere Situation ist, dass der Arbeiter -Thread bei der Ausführung einer Aufgabe blockiert ist. Wenn es auf die Eingabedaten des Benutzers wartet, der Benutzer jedoch keine Daten eingibt, was dazu führt, dass der Thread ständig blockiert wird. Ein solcher Arbeiter -Thread ist nur im Namen und erledigt eigentlich keine Aufgaben. Wenn sich alle Threads im Thread -Pool in diesem Zustand befinden, kann der Thread -Pool keine neuen Aufgaben hinzufügen.
• Aufgabenüberlastung
Wenn eine große Anzahl von Aufgaben in der Warteschlange in der Warteschlange zum Worker -Thread ausgesetzt werden, können diese Aufgaben selbst zu viel Systemressourcen konsumieren und Ressourcenknappheit verursachen.
Zusammenfassend sollten bei Verwendung von Thread -Pools folgende Prinzipien befolgt werden:
1. Wenn die Aufgabe A während der Ausführung auf Aufgabe B synchron warten muss, ist Aufgabe A nicht geeignet, der Arbeitswarteschlange des Thread -Pools hinzugefügt zu werden. Wenn Sie darauf warten müssen, dass andere Aufgaben die Ergebnisse wie Aufgabe A zur Warteschlange ausführen, kann dies zu einer Sackgasse führen
2. Wenn eine Aufgabe blockiert und lange Zeit blockiert wird, sollte die Zeitüberschreitungszeit festgelegt werden, um eine dauerhafte Blockierung des Worker -Threads zu vermeiden und Fadenleckage zu verursachen. Wenn im Serverprogramm ein Thread darauf wartet, dass der Client eine Verbindung herstellt oder auf die vom Client gesendeten Daten wartet, kann dies zu einer Blockierung führen. Sie können die Zeit auf folgende Weise festlegen:
Rufen Sie die SetSoTimeOut -Methode von ServerSocket an, um die Zeitüberschreitungszeit so festzulegen, dass sich der Client verbindet.
Rufen Sie für jeden mit dem Kunden verbundenen Socket die SetSotimeOut -Methode des Socket an, um die Zeitüberschreitung festzulegen, die darauf wartet, dass der Kunden Daten sendet.
3. Verstehen Sie die Eigenschaften der Aufgabe und analysieren Sie, ob die Aufgabe Operationen ausführt, die häufig IO -Vorgänge blockieren, oder Operationen, die nicht blockieren. Ersteres nimmt CPU zeitweise ein, während letzteres eine höhere Nutzungsrate aufweist. Wie lange dauert es, um eine Aufgabe zu erledigen? Ist es eine kurzfristige Aufgabe oder eine langfristige Aufgabe? Klassifizieren Sie dann die Aufgaben nach den Eigenschaften der Aufgabe und fügen Sie dann der Arbeitswarteschlange verschiedener Threadpools verschiedene Arten von Aufgaben hinzu. Auf diese Weise kann jeder Thread -Pool entsprechend den Eigenschaften der Aufgabe zugewiesen und angepasst werden.
4. Die Größe des Fadenpools ändern. Die optimale Größe eines Fadenpools hängt hauptsächlich von der Anzahl der verfügbaren CPUs im System und den Eigenschaften von Aufgaben in der Arbeitswarteschlange ab. Wenn es nur eine Arbeitswarteschlange in einem System mit N -CPUs gibt und alle Aufgaben mit arithmetischer Natur sind (nicht blockieren), wird der maximale CPU -Gebrauch im Allgemeinen erhalten, wenn der Fadenpool N- oder N+1 -Arbeiterfäden aufweist.
Wenn die Arbeitswarteschlange Aufgaben enthält, die IO -Vorgänge ausführen und häufig blockieren, lassen Sie die Thread -Pool -Größe die Anzahl der verfügbaren CPUs überschreiten, da nicht alle Arbeitsthreads die ganze Zeit funktionieren. Wählen Sie eine typische Aufgabe aus und schätzen Sie dann das Verhältnis der Wartezeit zu der Zeit, in der die CPU tatsächlich einnimmt, um Operationen im Projekt auszuführen, das diese Aufgabe ausführt. Für ein System mit N -CPUs müssen ungefähr n*(1+wt/st) Threads eingestellt werden, um sicherzustellen, dass die CPU vollständig verwendet wird.
Natürlich ist die CPU -Auslastung nicht das einzige, was beim Anpassen von Fadenpools zu berücksichtigen ist. Mit zunehmender Anzahl von Thread -Poolarbeiten, Speicher oder anderen Ressourcenbeschränkungen, wie Steckdosen, geöffnete Dateihandles oder Datenbankverbindungen usw. Es ist erforderlich, sicherzustellen, dass die von Multithreads verbrauchten Systemressourcen im Rahmen der Ausdauer des Systems liegen.
5. Überlastung von Aufgaben vermeiden. Der Server sollte die Anzahl der gleichzeitigen Verbindungen der Kunden basierend auf der Tragfähigkeit des Systems einschränken. Wenn die Verbindung des Clients den Grenzwert überschreitet, kann der Server die Verbindung ablehnen und freundliche Eingabeaufforderungen erstellen oder die Warteschlangenlänge begrenzen.
Die obigen Implementierungsmethoden und Antworten auf Java -Threadpools sind alle Inhalte, die ich mit Ihnen geteilt habe. Ich hoffe, Sie können Ihnen eine Referenz geben und ich hoffe, Sie können wulin.com mehr unterstützen.