BlockingQueue mit dynamischer Anpassung der Anzahl von Producer und Consumer Threads

sirbender

Top Contributor
Hi,

In der Praxis hat es sich gezeigt, dass eine BlockingQueue immer fast voll oder fast leer ist. D.h. entweder werden Producer oder Consumer geblockt bzw. koennen nicht soviel Arbeit verrichten wie theoretisch moeglich.

Wenn man den Thread-Pool und die Queue enger verzahnt koennte man dem doch Abhilfe schaffen fuer bestimmte Arten von Arbeit (klappt nicht in allen Szenarien).

Ein Beispiel: Producer lesen eine Datei ein, parsen diese und generieren eine Berechnung die in die Queue gelegt wird. Producer nehmen diese Berechnung und fuehren sie aus.

Abhaengig von der Art der Dateien, der Auslastung der HDD, der Aufwaendigkeit der Berechnung und vielen weiteren Faktoren koennen manchmal die Consumer und manchmal die Producer schneller arbeiten. Die Queue ist also gestopft voll, oder praktisch leer.

Wuerde die Anzahl der Producer verringert und die Anzahl der Consumer erhoeht wenn die Queue fuer einige Zeit voll ist, waere der Durchsatz hoeher.
Wuerde die Anzahl der Producer erhoeht und die Anazahl der Consumer verringert wenn die Queue praktisch leer ist, koennte der Durchsatz ebenfalls erhoeht werden.

Eine Loesung aus eng verzahnter Thread-Pool und Queue koennte

1. die Absolute Zahl von Threads (Producer + Consumer) angepasst werden - z.B. wenn der Rechner ausgelastet ist.
2. die Anzahl der Producer und die Anzahl der Consumer Threads dynamisch angepasst werden.
Start: 12 Producer, 12 Consumer (Queue voll)
Update: 6 Producer, 18 Consumer (Queue immer noch voll)
Update: 3 Producer, 21 Consumer (Queue und Worker threads arbeiten optimal)
...
...
...
Situation hat sich geaendert. Queue ist praktisch leer.
Update: 6 Producer, 18 Consumer (Queue ist immer noch leer)
Update: 9 Producer, 15 Consumer (Queue und Worker threads sind optimal ausgelastet)


Gibt es solcher Loesungen? Spricht was dagegen?
 
Zuletzt bearbeitet:

httpdigest

Top Contributor
Hierfür gibt es die Concurrency Klassen in dem java.util.concurrent Package, die man sich mal angucken sollte.
Das Problem bei dem klassischen Producer/Consumer mit Threads und einer Queue ist, dass der Fokus nicht auf den zu erledigenden Arbeiten/Tasks liegt, sondern auf den Threads, die Tasks erledigen. Das heißt, man sagt dort eher "Ich habe genau N Producer und genau M Consumer, und die verrichten ihre Arbeit". Stattdessen kann man mit dem ForkJoin Framework das ganze anders ausdrücken: "Ich habe N bis M Tasks, die abgearbeitet werden sollen, wobei N bzw. M zuerst unbekannt sein kann und sich während der Abarbeitung der Tasks dynamisch ändern können (unbekannte Anzahl an Zeilen in einer Datei z.B.) und jetzt führe diese Tasks so aus, dass eine hohe Effizienz bzw. ein hoher Durchsatz erreicht wird."
Das heißt, mit ForkJoin Tasks formuliert man eher die Abhängigkeiten der zu erledigenden Aufgaben und überlässt die tatsächliche Ausführung/Abarbeitung dieser Aufgaben dann dem ForkJoin Executor Service. Dieser kann dann nach Belieben mit mehr oder weniger Threads skalieren und auch beliebige Aufgaben/Tasks (z.B. die eines klassischen Consumers und die eines klassischen Producers) auf beliebige Threads (nicht fest) verteilen - durch z.B. "Work Stealing".
Siehe: https://www.oracle.com/technetwork/articles/java/fork-join-422606.html

Möglicherweise wurden von den Betreibern des Forums ungekennzeichnete Werbetexte in meinen Beitrag eingefügt, die nicht als solche erkennbar sind. Das können beispielsweise Kursempfehlungen sein. Ich distanziere mich davon und empfehle, solchen Links nicht zu folgen.
 

Ähnliche Java Themen

Neue Themen


Oben