Executors.newCachedThreadPool () versus Executors.newFixedThreadPool ()

newCachedThreadPool() versus newFixedThreadPool()

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

   

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

    newFixedThreadPool

    Erstellt einen Threadpool, der eine festgelegte Anzahl von Threads wiederverwendet, die von einer freigegebenen unbegrenzten Warteschlange ausgeführt werden. Zu jedem Zeitpunkt sind höchstens nThreads Threads aktive Verarbeitungsaufgaben. Wenn zusätzliche Aufgaben gesendet werden, wenn alle Threads aktiv sind, warten sie in der Warteschlange, bis ein Thread verfügbar ist. Wenn ein Thread aufgrund eines Fehlers während der Ausführung vor dem Herunterfahren beendet wird, wird ein neuer Thread verwendet, falls dies zur Ausführung nachfolgender Tasks erforderlich ist. Die Threads im Pool werden existieren, bis sie explizit beendet werden.

    newCachedThreadPool

    Erstellt einen Threadpool, der bei Bedarf neue Threads erstellt, aber zuvor erstellte Threads wieder verwendet, wenn sie verfügbar sind. Diese Pools verbessern normalerweise die performance von Programmen, die viele kurzlebige asynchrone Tasks ausführen. Auszuführende Aufrufe werden zuvor erstellte Threads wiederverwenden, wenn sie verfügbar sind. Wenn kein vorhandener Thread verfügbar ist, wird ein neuer Thread erstellt und zum Pool hinzugefügt. Threads, die seit 60 Sekunden nicht verwendet wurden, werden beendet und aus dem Cache entfernt. Daher wird ein Pool, der lange genug im Leerlauf bleibt, keine Ressourcen verbrauchen. Beachten Sie, dass Pools mit ähnlichen Eigenschaften, aber unterschiedlichen Details (z. B. Timeout-Parametern) mithilfe von ThreadPoolExecutor-Konstruktoren erstellt werden können.

    In Bezug auf die Ressourcen hält der newFixedThreadPool alle Threads so lange in Betrieb, bis sie explizit beendet werden. Im newCachedThreadPool Threads, die seit 60 Sekunden nicht verwendet wurden, werden beendet und aus dem Cache entfernt.

    Vor diesem Hintergrund hängt der Ressourcenverbrauch sehr stark von der Situation ab. Zum Beispiel, wenn Sie eine große Anzahl von lang laufenden Aufgaben haben, würde ich vorschlagen, die FixedThreadPool . Wie für das CachedThreadPool , sagen die Dokumente, dass “diese Pools in der Regel die performance von Programmen verbessern, die viele kurzlebige asynchrone Aufgaben ausführen”.

    Um die anderen Antworten zu vervollständigen, möchte ich Effektive Java, 2. Ausgabe, von Joshua Bloch, Kapitel 10, Item 68 zitieren:

    “Die Auswahl des Executor-Service für eine bestimmte Anwendung kann schwierig sein. Wenn Sie ein kleines Programm oder einen leicht ausgelasteten Server schreiben, ist Executors.new- CachedThreadPool im Allgemeinen eine gute Wahl , da es keine Konfiguration erfordert und im Allgemeinen” Richtig. “Aber ein Cache-Thread-Pool ist keine gute Wahl für einen stark ausgelasteten Produktionsserver !

    In einem zwischengespeicherten Thread-Pool werden gesendete Aufgaben nicht in die Warteschlange gestellt, sondern sofort einem Thread zur Ausführung übergeben. Wenn keine Threads verfügbar sind, wird ein neues erstellt . Wenn ein Server so stark ausgelastet ist, dass alle seine CPUs voll ausgelastet sind und mehr Aufgaben ankommen, werden mehr Threads erstellt, was die Dinge nur verschlimmern wird.

    Daher ist es bei einem stark ausgelasteten Produktionsserver viel besser, wenn Sie Executors.newFixedThreadPool verwenden , wodurch Sie einen Pool mit einer festen Anzahl von Threads erhalten oder die ThreadPoolExecutor-class direkt für maximale Kontrolle verwenden.

    Wenn Sie den Code im grepcode sehen, werden Sie sehen, dass sie ThreadPoolExecutor aufrufen . intern und ihre Eigenschaften einstellen. Sie können Ihr Konto erstellen, um Ihre Anforderungen besser steuern zu können.

     public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); } public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue()); } 

    Das ist richtig, Executors.newCachedThreadPool() ist keine gute Wahl für Server-Code, der mehrere Clients und gleichzeitige Anfragen bedient.

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

    1. Es ist unbegrenzt, was bedeutet, dass Sie die Tür für jeden öffnen, der Ihre JVM lähmt, indem Sie einfach mehr Arbeit in den Dienst injizieren (DoS-Attacke). Threads verbrauchen eine nicht zu vernachlässigende Menge an Arbeitsspeicher und erhöhen auch den Arbeitsspeicherverbrauch aufgrund ihrer laufenden Arbeit, so dass es ziemlich einfach ist, einen Server auf diese Weise zu stürzen (es sei denn, Sie haben andere performancesschalter installiert).

    2. Das unbegrenzte Problem wird durch die Tatsache verstärkt, dass der Executor von einer SynchronousQueue konfrontiert wird, was bedeutet, dass es eine direkte Übergabe zwischen dem Aufgabengeber und dem Thread-Pool gibt. Jede neue Aufgabe erstellt einen neuen Thread, wenn alle vorhandenen Threads beschäftigt sind. Dies ist im Allgemeinen eine schlechte Strategie für den Servercode. Wenn die CPU gesättigt ist, benötigen vorhandene Aufgaben länger. Es werden jedoch noch mehr Aufgaben eingereicht und mehr Threads erstellt, sodass Aufgaben länger und länger dauern. Wenn die CPU gesättigt 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 eingestellten maximalen Anzahl von Threads;

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

    Aber ThreadPoolExecutor bietet im Vergleich zu newFixedThreadPool oder newCachedThreadPool flexiblere functionen

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

    Vorteile:

    1. Sie haben die volle Kontrolle über die BlockingQueue- Größe. Es ist nicht anders als in früheren zwei Optionen. Ich werde keinen Speichererrors aufgrund einer großen Anhäufung von anstehenden Callable / Runnable-Tasks in unerwarteten Turbulenzen im System bekommen.

    2. Sie können eine benutzerdefinierte Ablehnungsrichtlinie implementieren ODER eine der folgenden Richtlinien verwenden:

      1. In der standardmäßigen ThreadPoolExecutor.AbortPolicy gibt der Handler beim Zurückweisen eine Runtime RejectedExecutionException aus.

      2. In ThreadPoolExecutor.CallerRunsPolicy führt der Thread, der ThreadPoolExecutor.CallerRunsPolicy selbst ausführt, den Task aus. Dies bietet einen einfachen Feedback-Kontrollmechanismus, der die Geschwindigkeit, mit der neue Aufgaben übermittelt werden, verlangsamt.

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

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

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

      1. Um einen beschreibenden Thread-Namen festzulegen
      2. So legen Sie den Thread-Daemon-Status fest
      3. So legen Sie die Thread-Priorität fest

    Sie müssen newCachedThreadPool nur verwenden, wenn Sie kurzlebige asynchrone Tasks wie in Javadoc angegeben haben. Wenn Sie Tasks senden, deren Verarbeitung länger dauert, werden Sie zu viele Threads erstellen. Sie können 100% CPU erreichen, wenn Sie lange laufende Aufgaben mit höherer Geschwindigkeit an newCachedThreadPool senden ( http://rashcoder.com/be-careful-while-using-executors-newcachedthreadpool/ ).

    Ich mache ein paar schnelle Tests und habe folgende Erkenntnisse:

    1) wenn SynchroneQueue verwendet wird:

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

    Ausnahme im Thread “main” java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@3fee733d abgelehnt von java.util.concurrent.ThreadPoolExecutor@5acf9800 [Wird ausgeführt, Poolgröße = 3, aktive Threads = 3, Aufgaben in der Warteschlange = 0, abgeschlossene Aufgaben = 0]

    bei java.util.concurrent.ThreadPoolExecutor $ AbortPolicy.rejectedExecution (ThreadPoolExecutor.java:2047)

    2) wenn LinkedBlockingQueue verwendet wird:

    Die Anzahl der Threads wird nie von der Mindestgröße zur Maximalgröße erhöht, dh der Threadpool hat eine feste Größe als Mindestgröße.