8

Ich habe Java Testamentsvollstrecker in meinem Multi-Threading-Anwendungen verwendet, aber ich kann nicht scheinen, um herauszufinden, wann die beste ist jede der folgenden Arten zu verwenden:Wie verwende ich Java Executor richtig?

1.

ExecutorService executor=Executors.newFixedThreadPool(50); 
executor.execute(new A_Runner(... some parameter ...)); 
executor.shutdown(); 
while (!executor.isTerminated()) { Thread.sleep(100); } 
.
int Page_Count=200; 
ExecutorService executor=Executors.newFixedThreadPool(50); 
doneSignal=new CountDownLatch(Page_Count); 
for (int i=0;i<Page_Count;i++) executor.execute(new A_Runner(doneSignal, ... some parameter ...)); 
doneSignal.await(); 
executor.shutdown(); 
while (!executor.isTerminated()) { Thread.sleep(100); } 

3.

int Executor_Count=30; 
ThreadPoolExecutor executor=new ThreadPoolExecutor(Executor_Count,Executor_Count*2,1,TimeUnit.SECONDS,new LinkedBlockingQueue()); 
List<Future<String>> futures=new ArrayList<>(3330); 

for (int i=0;i<50;i++) futures.add(executor.submit(new A_Runner(... some parameter ...)); 
executor.shutdown(); 
while (!executor.isTerminated()) { executor.awaitTermination(1,TimeUnit.SECONDS); } 
for (Future<String> future : futures) 
{ 
    String f=future.get(); 
    // ... 
} 

Insbesondere in [2] was passiert, wenn ich überspringen das doneSignal, dann wird es wie [1] sein, also was nützt das doneSignal?

Auch in [3], was ist, wenn ich ein doneSignal hinzufügen? Oder ist es möglich?

Was ich gerne wissen würde: Sind diese Ansätze austauschbar oder gibt es eine bestimmte Situation, in der ich einen bestimmten Typ verwenden soll?

Antwort

10
  1. ExecutorService

    ExecutorService executor=Executors.newFixedThreadPool(50);

    Es ist einfach und leicht zu bedienen. Es verbirgt Low Level Details von ThreadPoolExecutor.

    Bevorzugen Sie diese, wenn die Anzahl der Callable/Runnable Aufgaben in der Anzahl klein ist und das Stapeln von Aufgaben in unbegrenzter Warteschlange den Arbeitsspeicher nicht erhöht. & verschlechtern die Leistung des Systems. Wenn Sie CPU/Memory Constraints haben, verwenden Sie ThreadPoolExecutor mit Kapazitätsbeschränkungen & RejectedExecutionHandler, um die Ablehnung von Aufgaben zu behandeln.

  2. CountDownLatch

    Sie haben CountDownLatch mit einer gegebenen Zählung initialisiert. Diese Anzahl wird durch Aufrufe der countDown()-Methode verringert. Ich nehme an, dass Sie später in Ihrer ausführbaren Aufgabe Dekrement aufrufen. Threads, die darauf warten, dass diese Anzahl Null erreicht, können eine der Methoden await() aufrufen. Der Aufruf von await() blockiert den Thread, bis die Zählung Null erreicht. Diese Klasse ermöglicht einem Java-Thread zu warten, bis andere Threads ihre Aufgaben abgeschlossen haben.

    Use Cases:

    1. Achieving Maximum Parallelismus: Manchmal wollen wir eine Anzahl von Threads zur gleichen Zeit starten

    2. Warten N Threads maximale Parallelität vor dem Start abgeschlossen zu erreichen Ausführung

    3. Deadlock-Erkennung.

      Werfen Sie einen Blick auf diese article von Lokesh Gupta für weitere Details.

  3. ThreadPoolExecutor: Es bietet mehr Kontrolle verschiedene Thread-Pool-Parameter finetune. Wenn Ihre Anwendung durch die Anzahl der aktiven Aufgaben Runnable/Callable eingeschränkt ist, sollten Sie die beschränkte Warteschlange verwenden, indem Sie die maximale Kapazität festlegen. Sobald die Warteschlange die maximale Kapazität erreicht hat, können Sie Abweisungs-Handler definieren. Java bietet vier Arten von RejectedExecutionHandlerpolicies.

    1. In dem Standard ThreadPoolExecutor.AbortPolicy, wirft der HF eine Laufzeit RejectedExecutionException auf Ablehnung.

    2. In ThreadPoolExecutor.CallerRunsPolicy führt der Thread, der selbst ausführt, die Aufgabe 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. In ThreadPoolExecutor.DiscardOldestPolicy, wenn der Exekutor nicht heruntergefahren wird, wird die Aufgabe auf den Kopf der Arbeitswarteschlange fallen gelassen, und dann wird die Ausführung erneut versucht (die wiederum ausfallen können, wodurch diese zu wiederholen.)

      Wenn Sie das Verhalten CountDownLatch simulieren möchten, können Sie die Methode invokeAll() verwenden.

  4. Ein weiterer Mechanismus nicht Zitat tat, ist ForkJoinPool

    Die ForkJoinPool zu Java in Java 7 wurde hinzugefügt Die ForkJoinPool ist ähnlich die Java ExecutorService aber mit einem Unterschied. Die ForkJoinPool macht es einfach für Aufgaben, ihre Arbeit in kleinere Aufgaben aufzuteilen, die dann auch an die ForkJoinPool gesendet werden. Das Stehlen von Aufgaben geschieht in ForkJoinPool, wenn freie Worker-Threads Aufgaben aus der belegten Worker-Thread-Warteschlange stehlen.

    Java 8 hat eine weitere API in ExecutorService eingeführt, um Arbeitsstehlingpool zu erstellen. Sie müssen RecursiveTask und RecursiveAction nicht erstellen, können aber weiterhin ForkJoinPool verwenden.

    public static ExecutorService newWorkStealingPool() 
    

    Erstellt eine Arbeit stiehlt Thread-Pool alle verfügbaren Prozessoren als Ziel Parallelität Ebene verwendet wird.

    Standardmäßig wird die Anzahl der CPU-Kerne als Parameter verwendet.

Alle diese vier Mechanismen sind komplementär zueinander. Abhängig von der Granularität, die Sie steuern möchten, müssen Sie die richtigen auswählen.