Es scheint unmöglich zu sein, einen zwischengespeicherten Thread-Pool mit einer Begrenzung der Anzahl der Threads zu erstellen, die er erstellen kann.
So wird statisches Executors.newCachedThreadPool in der Standard-Java-Bibliothek implementiert:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
Verwenden Sie diese Vorlage, um einen zwischengespeicherten Thread-Pool mit fester Größe zu erstellen:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronusQueue<Runable>());
Wenn Sie dies jetzt verwenden und 3 Aufgaben einreichen, ist alles in Ordnung. Das Senden weiterer Aufgaben führt zu abgelehnten Ausführungsausnahmen.
Versuchen Sie dies:
new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runable>());
Wird dazu führen, dass alle Threads nacheinander ausgeführt werden. Das heißt, der Thread-Pool wird niemals mehr als einen Thread erstellen, um Ihre Aufgaben zu erledigen.
Dies ist ein Fehler in der Ausführungsmethode von ThreadPoolExecutor? Oder ist das vielleicht beabsichtigt? Oder gibt es einen anderen Weg?
Bearbeiten: Ich möchte genau so etwas wie den zwischengespeicherten Thread-Pool (er erstellt Threads bei Bedarf und beendet sie dann nach einer gewissen Zeit), jedoch mit einer Begrenzung der Anzahl der Threads, die er erstellen kann, und der Möglichkeit, weitere Aufgaben in die Warteschlange zu stellen, sobald er vorhanden ist das Thread-Limit erreicht. Nach der Antwort von sjlee ist dies unmöglich. Wenn man sich die execute () -Methode von ThreadPoolExecutor ansieht, ist dies in der Tat unmöglich. Ich müsste ThreadPoolExecutor in eine Unterklasse unterteilen und execute () überschreiben, ähnlich wie SwingWorker, aber was SwingWorker in seiner execute () tut, ist ein vollständiger Hack.
quelle
Antworten:
Der ThreadPoolExecutor weist die folgenden Hauptverhaltensweisen auf, und Ihre Probleme können durch diese Verhaltensweisen erklärt werden.
Wenn Aufgaben eingereicht werden,
Beachten Sie im ersten Beispiel, dass die SynchronousQueue im Wesentlichen die Größe 0 hat. Sobald Sie die maximale Größe (3) erreicht haben, wird die Ablehnungsrichtlinie aktiviert (# 4).
Im zweiten Beispiel ist die Warteschlange der Wahl eine LinkedBlockingQueue mit einer unbegrenzten Größe. Daher bleiben Sie bei Verhalten Nr. 2 hängen.
Sie können nicht wirklich viel mit dem zwischengespeicherten Typ oder dem festen Typ basteln, da deren Verhalten fast vollständig bestimmt ist.
Wenn Sie einen begrenzten und dynamischen Thread-Pool haben möchten, müssen Sie eine positive Kerngröße und eine maximale Größe in Kombination mit einer Warteschlange endlicher Größe verwenden. Beispielsweise,
Nachtrag : Dies ist eine ziemlich alte Antwort, und es scheint, dass JDK sein Verhalten in Bezug auf die Kerngröße 0 geändert hat. Seit JDK 1.6 fügt der ThreadPoolExecutor a hinzu, wenn die Kerngröße 0 ist und der Pool keine Threads enthält Thread, um diese Aufgabe auszuführen. Daher ist die Kerngröße 0 eine Ausnahme von der obigen Regel. Dank Steve für bringen , dass meine Aufmerksamkeit.
quelle
allowCoreThreadTimeOut
, um diese Antwort perfekt zu machen. Siehe die Antwort von @ user1046052Sofern ich nichts verpasst habe, ist die Lösung der ursprünglichen Frage einfach. Der folgende Code implementiert das gewünschte Verhalten, wie im Originalposter beschrieben. Es werden bis zu 5 Threads erzeugt, um an einer unbegrenzten Warteschlange zu arbeiten, und inaktive Threads werden nach 60 Sekunden beendet.
quelle
Hatte das gleiche Problem. Da keine andere Antwort alle Fragen zusammenfasst, füge ich meine hinzu:
Es ist jetzt klar in Dokumenten geschrieben : Wenn Sie eine Warteschlange verwenden, die die
LinkedBlockingQueue
Einstellung für ( ) max-Threads nicht blockiert , hat dies keine Auswirkung, werden nur Kernthreads verwendet.so:
Dieser Testamentsvollstrecker hat:
Kein Konzept für maximale Threads, da wir eine unbegrenzte Warteschlange verwenden. Dies ist eine gute Sache, da eine solche Warteschlange dazu führen kann, dass der Executor eine große Anzahl von nicht zum Kern gehörenden zusätzlichen Threads erstellt, wenn er seiner üblichen Richtlinie folgt.
Eine Warteschlange mit maximaler Größe
Integer.MAX_VALUE
.Submit()
wird geworfen,RejectedExecutionException
wenn die Anzahl der ausstehenden Aufgaben überschritten wirdInteger.MAX_VALUE
. Ich bin mir nicht sicher, ob uns zuerst der Speicher ausgeht, sonst passiert dies.Hat 4 Kerngewinde möglich. Leerlauf-Core-Threads werden automatisch beendet, wenn sie 5 Sekunden lang inaktiv sind. Also, ja, ausschließlich bei Bedarf. Threads können mithilfe der
setThreads()
Methode variiert werden .Stellt sicher, dass die minimale Anzahl von Kernthreads niemals kleiner als eins ist, da sonst
submit()
jede Aufgabe abgelehnt wird. Da Kernthreads> = max threads sein müssen,setThreads()
legt die Methode auch max threads fest, obwohl die maximale Threadeinstellung für eine unbegrenzte Warteschlange unbrauchbar ist.quelle
In Ihrem ersten Beispiel werden nachfolgende Aufgaben abgelehnt, da dies
AbortPolicy
die Standardeinstellung istRejectedExecutionHandler
. Der ThreadPoolExecutor enthält die folgenden Richtlinien, die Sie über diesetRejectedExecutionHandler
Methode ändern können :Es hört sich so an, als ob Sie einen zwischengespeicherten Thread-Pool mit einer CallerRunsPolicy möchten.
quelle
Keine der Antworten hier hat mein Problem behoben, das mit dem Erstellen einer begrenzten Anzahl von HTTP-Verbindungen mit dem HTTP-Client von Apache (Version 3.x) zu tun hatte. Da ich einige Stunden gebraucht habe, um ein gutes Setup zu finden, werde ich Folgendes mitteilen:
Dadurch wird ein erstellt,
ThreadPoolExecutor
der mit fünf beginnt und maximal zehn gleichzeitig ausgeführte Threads enthält, dieCallerRunsPolicy
zur Ausführung verwendet werden.quelle
Per Javadoc für ThreadPoolExecutor:
(Hervorhebung von mir.)
Die Antwort von Jitter ist, was Sie wollen, obwohl meine Ihre andere Frage beantwortet. :) :)
quelle
Es gibt noch eine Option. Anstatt die neue SynchronousQueue zu verwenden, können Sie auch jede andere Warteschlange verwenden. Sie müssen jedoch sicherstellen, dass die Größe 1 ist, damit der Executorservice gezwungen wird, einen neuen Thread zu erstellen.
quelle
Sieht nicht so aus, als ob eine der Antworten die Frage tatsächlich beantwortet - tatsächlich sehe ich keinen Weg, dies zu tun -, selbst wenn Sie eine Unterklasse von PooledExecutorService haben, da viele der Methoden / Eigenschaften privat sind, z Mach Folgendes:
Das nächste, was ich bekam, war das - aber selbst das ist keine sehr gute Lösung
ps hat das oben nicht getestet
quelle
Hier ist eine andere Lösung. Ich denke, diese Lösung verhält sich so, wie Sie es möchten (obwohl Sie nicht stolz auf diese Lösung sind):
quelle
Das ist was du willst (zumindest denke ich das). Eine Erklärung finden Sie in der Antwort von Jonathan Feinberg
Executors.newFixedThreadPool(int n)
quelle
Sie können
ThreadPoolExecutor
wie von @sjlee vorgeschlagen verwendenSie können die Größe des Pools dynamisch steuern. Schauen Sie sich diese Frage für weitere Details an:
Dynamischer Thread-Pool
ODER
Sie können die newWorkStealingPool- API verwenden, die mit Java 8 eingeführt wurde.
Standardmäßig ist die Parallelitätsstufe auf die Anzahl der CPU-Kerne in Ihrem Server eingestellt. Wenn Sie einen 4-Kern-CPU-Server haben, beträgt die Größe des Thread-Pools 4. Diese API gibt den
ForkJoinPool
Typ vonExecutorService
und ermöglicht das Stehlen von inaktiven Threads durch das Stehlen von Aufgaben von ausgelasteten Threads in ForkJoinPool.quelle
Das Problem wurde wie folgt zusammengefasst:
Bevor ich auf die Lösung verweise, werde ich erklären, warum die folgenden Lösungen nicht funktionieren:
Dadurch werden keine Aufgaben in die Warteschlange gestellt, wenn das Limit von 3 erreicht ist, da SynchronousQueue per Definition keine Elemente enthalten kann.
Dadurch wird nicht mehr als ein einzelner Thread erstellt, da ThreadPoolExecutor nur dann Threads erstellt, die die corePoolSize überschreiten, wenn die Warteschlange voll ist. LinkedBlockingQueue ist jedoch niemals voll.
Dadurch werden Threads erst wiederverwendet, wenn corePoolSize erreicht wurde, da ThreadPoolExecutor die Anzahl der Threads erhöht, bis corePoolSize erreicht ist, auch wenn vorhandene Threads inaktiv sind. Wenn Sie mit diesem Nachteil leben können, ist dies die einfachste Lösung für das Problem. Dies ist auch die in "Java Concurrency in Practice" (Fußnote auf S. 172) beschriebene Lösung.
Die einzige vollständige Lösung für das beschriebene Problem scheint darin zu bestehen, die
offer
Methode der Warteschlange zu überschreiben und eine zu schreiben,RejectedExecutionHandler
wie in den Antworten auf diese Frage erläutert: Wie kann der ThreadPoolExecutor die Anzahl der Threads vor dem Anstehen auf maximal erhöhen?quelle
Dies funktioniert für Java8 + (und andere, vorerst ..)
Dabei ist 3 die Grenze für die Anzahl der Threads und 5 das Zeitlimit für inaktive Threads.
Wenn Sie überprüfen möchten, ob es selbst funktioniert, finden Sie hier den Code für die Ausführung der Aufgabe:
Ausgabe des obigen Codes ist für mich
quelle