2013-03-18 10 views
16

My ThreadPoolExecutor kann neue Threads nicht erstellen. In der Tat schrieb ich einen etwas hacky LinkedBlockingQueue, der jede Aufgabe annehmen wird (dh es ist unbegrenzt), aber rufen Sie einen zusätzlichen Handler - der in meiner Anwendung Warnung Spur ausgibt, dass der Pool ist hinter - das gibt mir sehr explizite Informationen, die das TPE ablehnt Erstellen Sie neue Threads, obwohl die Warteschlange Tausende von Einträgen enthält. Mein Konstruktor ist wie folgt:ThreadPoolExecutor mit unbegrenzter Warteschlange erstellt keine neuen Threads

private final ExecutorService s3UploadPool = 
new ThreadPoolExecutor(1, 40, 1, TimeUnit.HOURS, unboundedLoggingQueue); 

Warum ist es zu schaffen keine neue Themen?

+0

Im Zusammenhang mit http://stackoverflow.com/questions/19528304/how-to-get-the-threadpoolexecutor-to-increase-threads-to-max-before-queueing/19528305#19528305 – Gray

Antwort

16

Diese Gotcha wird in this blog post bedeckt:

Diese Konstruktion von Thread-Pool wird einfach nicht wie erwartet funktionieren. Dies ist auf die Logik innerhalb des ThreadPoolExecutors zurückzuführen, bei der neue Threads hinzugefügt werden, wenn die Warteschlange nicht mit einer Aufgabe versehen werden kann. In unserem Fall verwenden wir eine unbegrenzte LinkedBlockingQueue, in der wir der Warteschlange immer eine Aufgabe anbieten können. Es bedeutet effektiv, dass wir niemals über die Kernpoolgröße und bis zur maximalen Poolgröße hinauswachsen werden.

Wenn Sie auch die minimalen von den maximalen Poolgrößen entkoppeln müssen, müssen Sie einige erweiterte Codierung vornehmen. Mir ist keine Lösung bekannt, die in den Java-Bibliotheken oder Apache Commons existiert. Die Lösung besteht darin, einen gekoppelten BlockingQueue zu erstellen, der das TPE kennt und eine Task ablehnt, wenn er weiß, dass das TPE keine Threads mehr hat, und dann manuell requeue. Es ist detaillierter in verlinkten Post abgedeckt. Letztendlich wird Ihre Konstruktion wie folgt aussehen:

public static ExecutorService newScalingThreadPool(int min, int max, long keepAliveTime) { 
    ScalingQueue queue = new ScalingQueue(); 
    ThreadPoolExecutor executor = 
     new ScalingThreadPoolExecutor(min, max, keepAliveTime, TimeUnit.MILLISECONDS, queue); 
    executor.setRejectedExecutionHandler(new ForceQueuePolicy()); 
    queue.setThreadPoolExecutor(executor); 
    return executor; 
} 

jedoch einfacher corePoolSize-maxPoolSize gesetzt und keine Sorge über diesen Unsinn.

+0

beachten Sie, Sie können immer noch erhalten Sie einen begrenzten Skalierungseffekt, indem Sie Core-Threads Zeitüberschreitung erlauben (Sie können von 0 bis zu maximalen Threads skalieren). – jtahlborn

+0

Gemäß den Javadocs: Indem Sie corePoolSize und maximumPoolSize gleich setzen, erstellen Sie einen Threadpool mit fester Größe. Wenn Sie also Ihrem letzten Satz folgen, wäre es besser, wenn Sie nur Executors.newFixedThreadPool (poolSize) verwenden. – darrickc

+0

@darrickc sieht so aus, als wäre Ihre Bearbeitung bereits abgelehnt worden, aber es wäre angemessen, sie in den Kommentar aufzunehmen. Ich glaube nicht, dass es gültig ist, weil ich in meinen Anwendungsfällen nicht möchte, dass Threads auslaufen. – djechlin

3

Wie von @djechlin erwähnt, ist dies Teil des (überraschend zu vielen) definierten Verhaltens der ThreadPoolExecutor. Ich glaube, dass ich eine etwas elegantere Lösung, um dieses Verhalten gefunden habe, die ich hier in meiner Antwort zeigen:

How to get the ThreadPoolExecutor to increase threads to max before queueing?

Sie LinkedBlockingQueue Grundsätzlich erweitern, um es für queue.offer(...) immer false zurück, die ein fügt zusätzliche Threads zum Pool, falls erforderlich. Wenn der Pool bereits bei max. Threads ist und alle besetzt sind, wird RejectedExecutionHandler aufgerufen. Es ist der Handler, der dann die put(...) in die Warteschlange einträgt.

Siehe meinen Code dort.

+1

Danke. Fest. @kevinarpe. – Gray

3

Es gibt eine Problemumgehung für dieses Problem. Betrachten Sie die folgende Umsetzung:

int corePoolSize = 40; 
int maximumPoolSize = 40; 
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 
    60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()); 
threadPoolExecutor.allowCoreThreadTimeOut(true); 

Durch die allowCoreThreadTimeOut() zu true Einstellung werden die Fäden in dem Pool erlaubt nach der angegebenen Zeitüberschreitung (60 Sekunden in diesem Beispiel) zu beenden. Bei dieser Lösung ist es das Konstruktorargument corePoolSize, das die maximale Poolgröße in der Praxis bestimmt, da der Threadpool auf corePoolSize anwächst und dann beginnt, Jobs zur Warteschlange hinzuzufügen. Es ist wahrscheinlich, dass der Pool niemals größer wird, da der Pool keine neuen Threads erzeugt, bis die Warteschlange voll ist (was dazu führen kann, dass LinkedBlockingQueue eine Kapazität von Integer.MAX_VALUE nicht hat).Folglich ist es wenig sinnvoll, maximumPoolSize auf einen größeren Wert als corePoolSize zu setzen.

Hinweis: Der Thread-Pool hat nach Ablauf des Timeouts 0 freie Threads, was bedeutet, dass es eine gewisse Latenzzeit gibt, bevor die Threads erstellt werden (normalerweise hätten Sie immer corePoolSize Threads zur Verfügung). Weitere Details finden Sie im JavaDoc von ThreadPoolExecutor.

Verwandte Themen