newCachedThreadPool()
gegen newFixedThreadPool()
Wann sollte ich das eine oder andere verwenden? Welche Strategie ist in Bezug auf die Ressourcennutzung besser?
newCachedThreadPool()
gegen newFixedThreadPool()
Wann sollte ich das eine oder andere verwenden? Welche Strategie ist in Bezug auf die Ressourcennutzung besser?
Ich denke, die Dokumente erklären den Unterschied und die Verwendung dieser beiden Funktionen ziemlich gut:
Erstellt einen Thread-Pool, der eine feste Anzahl von Threads wiederverwendet, die in einer gemeinsam genutzten unbegrenzten Warteschlange ausgeführt werden. Zu jedem Zeitpunkt sind höchstens nThreads-Threads aktive Verarbeitungsaufgaben. Wenn zusätzliche Aufgaben gesendet werden, während alle Threads aktiv sind, warten sie in der Warteschlange, bis ein Thread verfügbar ist. Wenn ein Thread aufgrund eines Fehlers während der Ausführung vor dem Herunterfahren beendet wird, wird bei Bedarf ein neuer Thread ersetzt, um nachfolgende Aufgaben auszuführen. Die Threads im Pool bleiben bestehen, bis sie explizit heruntergefahren werden.
Erstellt einen Thread-Pool, der nach Bedarf neue Threads erstellt, zuvor erstellte Threads jedoch wiederverwendet, sobald sie verfügbar sind. Diese Pools verbessern normalerweise die Leistung von Programmen, die viele kurzlebige asynchrone Aufgaben ausführen. Durch auszuführende Aufrufe werden zuvor erstellte Threads wiederverwendet, sofern verfügbar. Wenn kein vorhandener Thread verfügbar ist, wird ein neuer Thread erstellt und dem Pool hinzugefügt. Threads, die seit 60 Sekunden nicht mehr verwendet wurden, werden beendet und aus dem Cache entfernt. Ein Pool, der lange genug inaktiv bleibt, verbraucht daher keine Ressourcen. Beachten Sie, dass Pools mit ähnlichen Eigenschaften, aber unterschiedlichen Details (z. B. Timeout-Parametern) mithilfe von ThreadPoolExecutor-Konstruktoren erstellt werden können.
In Bezug auf die Ressourcen newFixedThreadPool
werden alle Threads so lange ausgeführt, bis sie explizit beendet werden. In den newCachedThreadPool
Threads, die seit 60 Sekunden nicht mehr verwendet wurden, werden sie beendet und aus dem Cache entfernt.
Vor diesem Hintergrund wird der Ressourcenverbrauch sehr stark von der Situation abhängen. Zum Beispiel, wenn Sie eine große Anzahl von Aufgaben mit langer Laufzeit haben, würde ich die vorschlagen FixedThreadPool
. In CachedThreadPool
den Dokumenten heißt es: "Diese Pools verbessern normalerweise die Leistung von Programmen, die viele kurzlebige asynchrone Aufgaben ausführen."
newCachedThreadPool
dass dies einige schwerwiegende Probleme verursachen kann, da Sie die gesamte Kontrolle demthread pool
und überlassen, wenn der Dienst mit anderen auf demselben Host arbeitet , was dazu führen kann, dass die anderen aufgrund des langen Wartens der CPU abstürzen. Ich denkenewFixedThreadPool
also, dass dies in einem solchen Szenario sicherer sein kann. Auch dieser Beitrag verdeutlicht die herausragendsten Unterschiede zwischen ihnen.Um die anderen Antworten zu vervollständigen, möchte ich Effective Java, 2. Auflage, von Joshua Bloch, Kapitel 10, Punkt 68, zitieren:
quelle
Wenn Sie sich den Quellcode ansehen , werden Sie sehen, dass sie ThreadPoolExecutor aufrufen . intern und Einstellung ihrer Eigenschaften. Sie können Ihre erstellen, um Ihre Anforderungen besser kontrollieren zu können.
quelle
Wenn Sie sich keine Sorgen über eine unbegrenzte Warteschlange an aufrufbaren / ausführbaren Aufgaben machen, können Sie eine davon verwenden. Wie von bruno vorgeschlagen, ziehe ich es auch
newFixedThreadPool
zunewCachedThreadPool
mehr als diese beiden.Aber ThreadPoolExecutor bietet flexiblere Funktionen im Vergleich zu entweder
newFixedThreadPool
odernewCachedThreadPool
Vorteile:
Sie haben die volle Kontrolle über die BlockingQueue- Größe. Im Gegensatz zu den beiden vorherigen Optionen ist es nicht unbegrenzt. Ich bekomme keinen Speicherfehler aufgrund einer großen Anzahl anstehender Callable / Runnable-Aufgaben, wenn unerwartete Turbulenzen im System auftreten.
Sie können eine benutzerdefinierte Ablehnungsbehandlungsrichtlinie implementieren ODER eine der folgenden Richtlinien verwenden:
Standardmäßig
ThreadPoolExecutor.AbortPolicy
löst der Handler bei Ablehnung eine Laufzeit-RejectedExecutionException aus.In
ThreadPoolExecutor.CallerRunsPolicy
führt der Thread, der die Ausführung selbst aufruft, die Aufgabe aus. Dies bietet einen einfachen Mechanismus zur Steuerung der Rückmeldung, der die Übermittlungsrate neuer Aufgaben verlangsamt.In
ThreadPoolExecutor.DiscardPolicy
wird eine Aufgabe, die nicht ausgeführt werden kann, einfach gelöscht.In
ThreadPoolExecutor.DiscardOldestPolicy
, wenn der Vollstrecker nicht heruntergefahren wird, wird die Aufgabe an der Spitze der Warteschlange gelöscht, und dann wird die Ausführung erneut versucht (die wiederum fehlschlagen kann, so dass diese wiederholt werden.)Sie können eine benutzerdefinierte Thread-Factory für die folgenden Anwendungsfälle implementieren:
quelle
Das ist richtig,
Executors.newCachedThreadPool()
keine gute Wahl für Servercode, der mehrere Clients und gleichzeitige Anforderungen bedient.Warum? Grundsätzlich gibt es zwei (verwandte) Probleme:
Es ist unbegrenzt, was bedeutet, dass Sie jedem die Tür öffnen, um Ihre JVM zu lähmen, indem Sie einfach mehr Arbeit in den Dienst einbringen (DoS-Angriff). Threads verbrauchen eine nicht zu vernachlässigende Menge an Speicher und erhöhen auch den Speicherverbrauch basierend auf ihren laufenden Arbeiten. Daher ist es recht einfach, einen Server auf diese Weise zu stürzen (es sei denn, Sie haben andere Leistungsschalter installiert).
Das unbegrenzte Problem wird durch die Tatsache verschärft, dass der Executor mit einem konfrontiert wird,
SynchronousQueue
was bedeutet, dass eine direkte Übergabe zwischen dem Task-Geber und dem Thread-Pool besteht. Jede neue Aufgabe erstellt einen neuen Thread, wenn alle vorhandenen Threads ausgelastet sind. Dies ist im Allgemeinen eine schlechte Strategie für Servercode. Wenn die CPU voll ist, dauert es länger, bis vorhandene Aufgaben abgeschlossen sind. Es werden jedoch immer mehr Aufgaben eingereicht und mehr Threads erstellt, sodass die Ausführung von Aufgaben immer länger dauert. Wenn die CPU gesättigt ist, sind mehr Threads definitiv nicht das, was der Server benötigt.Hier sind meine Empfehlungen:
Verwenden Sie einen Thread-Pool mit fester Größe Executors.newFixedThreadPool oder einen ThreadPoolExecutor. mit einer festgelegten maximalen Anzahl von Threads;
quelle
Die
ThreadPoolExecutor
Klasse ist die Basisimplementierung für die Executoren, die von vielenExecutors
Factory-Methoden zurückgegeben werden. Also lassen Sie uns nähern Feste und Cached Thread - Pools vonThreadPoolExecutor
‚s Perspektive.ThreadPoolExecutor
Der Hauptkonstruktor dieser Klasse sieht folgendermaßen aus:
Kernpoolgröße
Das
corePoolSize
bestimmt die Mindestgröße des Ziel-Thread-Pools. Die Implementierung würde einen Pool dieser Größe beibehalten, selbst wenn keine auszuführenden Aufgaben vorhanden sind.Maximale Poolgröße
Dies
maximumPoolSize
ist die maximale Anzahl von Threads, die gleichzeitig aktiv sein können.Nachdem der Thread-Pool gewachsen ist und den
corePoolSize
Schwellenwert überschritten hat, kann der Executor inaktive Threads beenden undcorePoolSize
erneut auf den Thread zugreifen . Wenn dies derallowCoreThreadTimeOut
Fall ist, kann der Executor sogar Core-Pool-Threads beenden, wenn sie mehr als denkeepAliveTime
Schwellenwert im Leerlauf waren .Wenn Threads also länger als der
keepAliveTime
Schwellenwert im Leerlauf bleiben , werden sie möglicherweise beendet, da keine Nachfrage nach ihnen besteht.Anstehen
Was passiert, wenn eine neue Aufgabe eingeht und alle Kernthreads belegt sind? Die neuen Aufgaben werden in dieser
BlockingQueue<Runnable>
Instanz in die Warteschlange gestellt . Wenn ein Thread frei wird, kann eine dieser Aufgaben in der Warteschlange verarbeitet werden.Es gibt verschiedene Implementierungen der
BlockingQueue
Schnittstelle in Java, so dass wir verschiedene Warteschlangenansätze implementieren können, wie:Begrenzte Warteschlange : Neue Aufgaben werden in einer begrenzten Aufgabenwarteschlange in die Warteschlange gestellt.
Ungebundene Warteschlange : Neue Aufgaben werden in einer unbegrenzten Aufgabenwarteschlange in die Warteschlange gestellt. Diese Warteschlange kann also so stark wachsen, wie es die Heap-Größe zulässt.
Synchrone Übergabe : Mit können Sie auch
SynchronousQueue
die neuen Aufgaben in die Warteschlange stellen. In diesem Fall muss beim Einreihen einer neuen Aufgabe bereits ein anderer Thread auf diese Aufgabe warten.Arbeitseinreichung
So
ThreadPoolExecutor
führt der eine neue Aufgabe aus:corePoolSize
Threads ausgeführt werden, wird versucht, einen neuen Thread mit der angegebenen Aufgabe als erstem Job zu starten.BlockingQueue#offer
Methode in die Warteschlange zu stellen . Dieoffer
Methode wird nicht blockiert, wenn die Warteschlange voll ist, und kehrt sofort zurückfalse
.offer
zurückgegeben wirdfalse
), wird versucht, dem Thread-Pool einen neuen Thread mit dieser Aufgabe als erstem Job hinzuzufügen.RejectedExecutionHandler
.Der Hauptunterschied zwischen den festen und zwischengespeicherten Thread-Pools beruht auf diesen drei Faktoren:
Fadenpool behoben
So funktioniert das
Excutors.newFixedThreadPool(n)
:Wie du siehst:
OutOfMemoryError
.Ein Thread-Pool mit fester Größe scheint ein guter Kandidat zu sein, wenn wir die Anzahl gleichzeitiger Aufgaben für Ressourcenverwaltungszwecke begrenzen möchten .
Wenn wir beispielsweise einen Executor verwenden, um Webserveranforderungen zu verarbeiten, kann ein fester Executor die Anforderungsbursts angemessener verarbeiten.
Für ein noch besseres Ressourcenmanagement wird dringend empfohlen, eine benutzerdefinierte Version
ThreadPoolExecutor
mit einer begrenztenBlockingQueue<T>
Implementierung und einer angemessenen Implementierung zu erstellenRejectedExecutionHandler
.Zwischengespeicherter Thread-Pool
So funktioniert das
Executors.newCachedThreadPool()
:Wie du siehst:
Integer.MAX_VALUE
. Praktisch ist der Thread-Pool unbegrenzt.SynchronousQueue
immer fehlschlägt, wenn am anderen Ende niemand ist, der sie akzeptiert!Verwenden Sie es, wenn Sie viele vorhersehbare kurzfristige Aufgaben haben.
quelle
Sie dürfen newCachedThreadPool nur verwenden, wenn Sie kurzlebige asynchrone Aufgaben haben, wie in Javadoc angegeben. Wenn Sie Aufgaben senden, deren Verarbeitung länger dauert, werden am Ende zu viele Threads erstellt. Sie können 100% der CPU erreichen, wenn Sie Aufgaben mit langer Laufzeit schneller an newCachedThreadPool senden ( http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/ ).
quelle
Ich mache einige Schnelltests und habe folgende Ergebnisse:
1) bei Verwendung von SynchronousQueue:
Nachdem die Threads die maximale Größe erreicht haben, werden alle neuen Arbeiten mit der folgenden Ausnahme abgelehnt.
2) bei Verwendung von LinkedBlockingQueue:
Die Threads werden niemals von minimaler Größe auf maximale Größe erhöht, was bedeutet, dass der Thread-Pool eine feste Größe als minimale Größe hat.
quelle