Es gibt etwas Seltsames an der Implementierung von BoundedExecutor
im Buch Java Concurrency in Practice.
Es soll die Übergabe von Aufgaben an den Executor drosseln, indem der übergebende Thread blockiert wird, wenn genügend Threads entweder in der Warteschlange stehen oder im Executor ausgeführt werden.
Dies ist die Implementierung (nach dem Hinzufügen der fehlenden Wiederholung in der Fangklausel):
%Vor% Wenn ich die BoundedExecutor
mit einer Executors.newCachedThreadPool()
und einer Grenze von 4 instanziiere, würde ich erwarten, dass die Anzahl der Threads, die durch den Cache-Thread-Pool instanziiert werden, niemals 4 überschreitet. In der Praxis jedoch tut es dies. Ich habe dieses kleine Testprogramm erstellt, um so viel wie 11 Threads zu erstellen:
Ich denke, es gibt einen winzigen kleinen Zeitrahmen zwischen der Freigabe des Semaphors und der Beendigung der Aufgabe, wo ein anderer Thread eine Genehmigung erwirbt und eine Aufgabe einreicht, während der Freigabethread noch nicht beendet ist. Mit anderen Worten, es hat eine Wettlaufbedingung.
Kann jemand das bestätigen?
Ich sehe so viel wie 9 Threads auf einmal erstellt. Ich vermute, dass es eine Race-Bedingung gibt, die dazu führt, dass mehr Thread als erforderlich ist.
Dies könnte daran liegen, dass vor und nach dem Ausführen der Aufgabe Arbeit zu erledigen ist. Das bedeutet, dass, obwohl nur 4 Threads in Ihrem Code-Block vorhanden sind, eine Reihe von Threads eine vorherige Aufgabe beendet oder bereit ist, eine neue Aufgabe zu starten.
d. Der Thread macht eine Freigabe () während er noch läuft. Auch wenn es das letzte ist, was du tust, ist es nicht das letzte, was es macht, bevor du eine neue Aufgabe erlangst.
BoundedExecutor war in der Tat als eine Veranschaulichung dafür gedacht, wie man die Aufgabeübertragung drosselt, und nicht als eine Möglichkeit, die Thread-Pool-Größe zu begrenzen. Es gibt direktere Wege, letzteres zu erreichen, wie zumindest ein Kommentar herausstellte.
Aber die anderen Antworten erwähnen nicht den Text in dem Buch, das eine unbegrenzte Warteschlange verwenden soll und
setzen Sie die Grenze für das Semaphor so, dass sie gleich der Poolgröße plus ist Anzahl der eingereihten Aufgaben, die Sie zulassen möchten, da der Semaphor ist die Anzahl der aktuell ausgeführten und wartenden Aufgaben begrenzen Ausführung. [JCiP, Ende von Abschnitt 8.3.3]
Durch Erwähnung der unbegrenzten Warteschlangen und der Poolgröße implizieren wir (anscheinend nicht sehr deutlich) die Verwendung eines Thread-Pools begrenzter Größe.
Was mich bei BoundedExecutor jedoch immer gestört hat, ist, dass es die ExecutorService-Schnittstelle nicht implementiert. Eine moderne Methode, um ähnliche Funktionen zu erreichen und dennoch die Standardschnittstellen zu implementieren, wäre die Verwendung von Guavas ListeningDecorator -Methode und ForwardingListeningExecutorService Klasse.
Sie haben in Ihrer Analyse der Wettlaufsituation Recht. Es gibt keine Synchronisationsgarantien zwischen dem ExecutorService & amp; der Semaphor.
Allerdings weiß ich nicht, ob der BoundedExecutor für die Anzahl der Threads gedrosselt wird. Ich denke, es ist mehr für die Drosselung der Anzahl der Aufgaben an den Dienst übermittelt. Stellen Sie sich vor, wenn Sie 5 Millionen Aufgaben haben, die Sie einreichen müssen, und wenn Sie mehr als 10.000 davon einreichen, haben Sie keinen Speicher mehr.
Nun, du wirst immer nur 4 Threads laufen haben, warum willst du alle 5 Millionen Aufgaben anstellen? Sie können ein ähnliches Konstrukt verwenden, um die Anzahl der Aufgaben zu begrenzen, die zu einem bestimmten Zeitpunkt in der Warteschlange stehen. Was Sie daraus machen sollten, ist, dass zu jedem Zeitpunkt nur 4 Aufgaben ausgeführt werden.
Offensichtlich ist die Lösung dafür, ein Executors.newFixedThreadPool(4)
zu verwenden.
Tags und Links java race-condition concurrency executor