it-swarm.com.de

Executors.newCachedThreadPool () versus Executors.newFixedThreadPool ()

newCachedThreadPool() versus newFixedThreadPool()

Wann sollte ich das eine oder andere verwenden? Welche Strategie ist in Bezug auf die Ressourcennutzung besser?

128
hakish

Ich denke, die Dokumente erklären den Unterschied und die Verwendung dieser beiden Funktionen ziemlich gut: 

newFixedThreadPool

Erstellt einen Thread-Pool, der eine .__ wiederverwendet. Feste Anzahl von Threads, die aus sind eine gemeinsam genutzte, unbegrenzte Warteschlange. Zu jedem beliebigen Punkt, höchstens nThreads-Threads aktive Bearbeitungsaufgaben sein. Ob Zusätzliche Aufgaben werden übergeben, wenn Alle Threads sind aktiv, sie warten in der Warteschlange, bis ein Thread .__ ist. verfügbar. Wenn ein Thread beendet wird wegen eines Fehlers während der Ausführung vor dem Herunterfahren wird ein neuer dauern seinen Platz, falls erforderlich nachfolgende Aufgaben. Die Threads in der Der Pool ist solange vorhanden, bis er explizit .__ ist. ausschalten. 

newCachedThreadPool

Erstellt einen Thread-Pool, der das neue .__ erstellt. Threads nach Bedarf, verwenden sie jedoch wieder zuvor konstruierte Threads, wenn Sie sind verfügbar. Diese Pools werden verbessern Sie normalerweise die Leistung von Programme, die viele kurzlebige .__ ausführen. asynchrone Aufgaben. Aufrufe zur Ausführung von wird das zuvor erstellte .__ wiederverwenden. falls vorhanden. Wenn kein vorhandener Thread verfügbar ist, wird ein neuer Thread erstellt und dem Pool hinzugefügt werden . Threads, die nicht für .__ verwendet wurden. sechzig Sekunden werden beendet und aus dem Cache entfernt. Also ein Pool das bleibt lange genug untätig verbrauchen keine Ressourcen. Beachten Sie, dass Pools mit ähnlichen Eigenschaften, aber Es können verschiedene Details (z. B. Timeout-Parameter) erstellt werden mit ThreadPoolExecutor-Konstruktoren.

In Bezug auf Ressourcen hält die newFixedThreadPool alle Threads so lange am Laufen, bis sie explizit beendet werden. In newCachedThreadPool Threads, die 60 Sekunden lang nicht verwendet wurden, werden beendet und aus dem Cache entfernt.

Daher hängt der Ressourcenverbrauch stark von der Situation ab. Wenn Sie zum Beispiel eine große Anzahl von lang laufenden Aufgaben haben, würde ich FixedThreadPool vorschlagen. In Bezug auf CachedThreadPool heißt es in den Dokumenten: "Diese Pools verbessern normalerweise die Leistung von Programmen, die viele kurzlebige asynchrone Aufgaben ausführen".

168
bruno conde

Um die anderen Antworten zu vervollständigen, möchte ich Effective Java, 2nd Edition, von Joshua Bloch, Kapitel 10, Punkt 68, zitieren:

"Die Auswahl des Executor-Dienstes für eine bestimmte Anwendung kann schwierig sein. Wenn Sie ein kleines Programm oder einen leicht geladenen Server schreiben, verwenden Sie Executors.new-CachedThreadPool im Allgemeinen ein gute Wahl , da es keine Konfiguration erfordert und im Allgemeinen "das Richtige tut". Aber ein zwischengespeicherter Thread-Pool ist keine gute Wahl für einen stark belasteten Produktionsserver

In einem zwischengespeicherten Threadpool , werden übergebene Aufgaben nicht in eine Warteschlange gestellt sondern sofort zur Ausführung an einen Thread übergeben. Wenn keine Threads verfügbar sind, wird ein neuer erstellt . Wenn ein Server so stark ausgelastet ist, dass alle seine CPUs voll ausgelastet sind und mehr Aufgaben eintreffen, werden mehr Threads erstellt, was die Sache nur verschlimmert.

Daher ist auf einem stark belasteten Produktionsserver die Verwendung von Executors.newFixedThreadPool viel besser, da Sie einen Pool mit einer festen Anzahl von Threads erhalten, oder die ThreadPoolExecutor-Klasse direkt für Steuerung. "

55
Louis F.

Wenn Sie den Code im grepcode sehen, werden Sie sehen, dass sie ThreadPoolExecutor aufrufen. intern und Einstellung ihrer Eigenschaften. Sie können Ihr Gerät erstellen, um Ihre Anforderungen besser zu steuern.

public static ExecutorService newFixedThreadPool(int nThreads) {
   return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
10
krmanish007

Das ist richtig, Executors.newCachedThreadPool() ist keine gute Wahl für Servercode, der mehrere Clients und gleichzeitige Anforderungen bedient.

Warum? Es gibt grundsätzlich zwei (verwandte) Probleme damit:

  1. Es ist unbegrenzt, was bedeutet, dass Sie die Tür öffnen, um Ihre JVM zu verkrüppeln, indem Sie einfach mehr Arbeit in den Dienst einfügen (DoS-Angriff). Threads verbrauchen eine nicht zu vernachlässigende Menge an Speicher und erhöhen auch den Speicherbedarf aufgrund ihrer laufenden Arbeit. Daher ist es ziemlich einfach, einen Server auf diese Weise zu stürzen (es sei denn, Sie verfügen über andere Leistungsschalter).

  2. Das unbegrenzte Problem wird durch die Tatsache verschärft, dass der Executor vor einer SynchronousQueue steht, was bedeutet, dass es eine direkte Übergabe zwischen dem Task-Geber und dem Thread-Pool gibt. Jede neue Aufgabe erstellt einen neuen Thread, wenn alle vorhandenen Threads belegt sind. Dies ist im Allgemeinen eine schlechte Strategie für Servercode. Wenn die CPU überlastet ist, dauert das Beenden vorhandener Aufgaben länger. Es werden jedoch noch mehr Aufgaben übermittelt und mehr Threads erstellt, sodass die Ausführung von Aufgaben länger und länger dauert. Wenn die CPU voll ist, sind mehr Threads definitiv nicht das, was der Server benötigt.

Hier sind meine Empfehlungen:

Verwenden Sie einen Threadpool mit fester Größe Executors.newFixedThreadPool oder einen ThreadPoolExecutor. mit einer festgelegten maximalen Anzahl von Threads;

8
Prashant Gautam

Wenn Sie sich keine Sorgen über die unbegrenzte Warteschlange der Aufgaben Callable/Runnable machen, können Sie eine davon verwenden. Wie von bruno vorgeschlagen, ziehe ich auch newFixedThreadPool der newCachedThreadPool zwischen diesen beiden vor.

ThreadPoolExecutor bietet jedoch flexiblere Funktionen als newFixedThreadPool oder newCachedThreadPool

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory,
RejectedExecutionHandler handler)

Vorteile:

  1. Sie haben volle Kontrolle über die Größe von BlockingQueue. Es ist nicht uneingeschränkt anders als bei den beiden vorherigen Optionen. Ich komme nicht aus dem Speicherfehler heraus, da sich die Callable/Runnable-Aufgaben in unerwarteten Turbulenzen im System befinden.

  2. Sie können eine benutzerdefinierte Ablehnungsbehandlung -Richtlinie OR mit einer der folgenden Richtlinien implementieren:

    1. In der Standardeinstellung ThreadPoolExecutor.AbortPolicy löst der Handler bei Ablehnung eine Laufzeit-RejectedExecutionException aus.

    2. In ThreadPoolExecutor.CallerRunsPolicy führt der Thread, der die Ausführung selbst ausführt, die Aufgabe aus. Dies bietet einen einfachen Mechanismus zur Regelung der Rückkopplung, der die Geschwindigkeit der Übergabe neuer Aufgaben verlangsamt.

    3. In ThreadPoolExecutor.DiscardPolicy wird eine Aufgabe, die nicht ausgeführt werden kann, einfach gelöscht.

    4. Wenn der Executor in ThreadPoolExecutor.DiscardOldestPolicy nicht heruntergefahren ist, wird die Task am Anfang der Arbeitswarteschlange gelöscht und die Ausführung wird erneut versucht (was erneut fehlschlagen kann, was dazu führt, dass dies wiederholt wird.)

  3. Sie können eine benutzerdefinierte Thread-Factory für die folgenden Anwendungsfälle implementieren:

    1. So legen Sie einen aussagekräftigeren Threadnamen fest
    2. So legen Sie den Thread-Daemon-Status fest
    3. Thread-Priorität einstellen
7
Ravindra babu

Sie müssen newCachedThreadPool nur verwenden, wenn Sie über kurzlebige asynchrone Tasks verfügen, wie in Javadoc angegeben. Wenn Sie Tasks übergeben, deren Verarbeitung länger dauert, werden am Ende zu viele Threads erstellt. Sie können 100% CPU erreichen, wenn Sie lange laufende Aufgaben schneller an newCachedThreadPool ( http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/ ) übergeben. 

3

Ich mache ein paar Schnelltests und habe folgende Erkenntnisse:

1) wenn Sie SynchronousQueue verwenden:

Nachdem die Threads die maximale Größe erreicht haben, wird jede neue Arbeit mit der folgenden Ausnahme abgelehnt.

Ausnahme im Thread "main" Java.util.concurrent.RejectedExecutionException: Task [email protected] wurde von [email protected] zurückgewiesen = 0, erledigte Aufgaben = 0]

at Java.util.concurrent.ThreadPoolExecutor $ AbortPolicy.rejectedExecution (ThreadPoolExecutor.Java:2047)

2) Wenn Sie LinkedBlockingQueue verwenden:

Die Threads werden nie von der Mindestgröße auf die Maximalgröße erhöht, was bedeutet, dass der Thread-Pool eine feste Größe als Mindestgröße hat.

0
Mike Lin