it-swarm.com.de

ExecutorService gegen ThreadPoolExecutor mit LinkedBlockingQueue

Ich arbeite an einem Multithread-Projekt, in dem ich mehrere Threads erzeugen muss, um die End-to-End-Leistung meines Client-Codes zu messen, während ich Last- und Leistungstests durchführe. Also habe ich den folgenden Code erstellt, der ExecutorService verwendet.

Unten ist der Code mit ExecutorService:

public class MultithreadingExample {

    public static void main(String[] args) throws InterruptedException {

        ExecutorService executor = Executors.newFixedThreadPool(20);
        for (int i = 0; i < 100; i++) {
            executor.submit(new NewTask());
        }

        executor.shutdown();
        executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
    }
}

class NewTask implements Runnable {

    @Override
    public void run() {
        //Measure the end to end latency of my client code
    }   
}

Problemstellung:

Jetzt las ich einen Artikel im Internet. Ich habe herausgefunden, dass es auch ThreadPoolExecutor gibt. So wurde ich verwirrt, welches ich benutzen sollte.

Wenn ich meinen obigen Code von ersetze:

ExecutorService executor = Executors.newFixedThreadPool(20);
    for (int i = 0; i < 100; i++) {
        executor.submit(new NewTask());
    }

zu:

BlockingQueue<Runnable> threadPool = new LinkedBlockingQueue<Runnable>();

ThreadPoolExecutor tpExecutor = new ThreadPoolExecutor(20, 2000, 0L, TimeUnit.MILLISECONDS, threadPool);

tpExecutor.prestartAllCoreThreads();

    for (int i = 0; i < 100; i++) {
        tpExecutor.execute(new NewTask());
    }

wird dies einen Unterschied machen? Ich versuche zu verstehen, was der Unterschied zwischen meinem ursprünglichen Code mit ExecutorService und dem neuen Code ist, der mit ThreadPoolExecutor eingefügt wurde. Einige meiner Teamkollegen sagten, der zweite (ThreadPoolExecutor) sei der richtige Weg.

Kann das jemand für mich klären?

48
user1813228

Hier ist die Quelle von Executors.newFixedThreadPool:

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

Es verwendet intern die Klasse ThreadPoolExecutor mit der Standardkonfiguration, wie Sie oben sehen können. Jetzt gibt es Szenarien, in denen die Standardkonfiguration nicht geeignet ist, sondern beispielsweise statt LinkedBlockingQueue eine Prioritätswarteschlange verwendet werden muss. In solchen Fällen kann der Anrufer direkt an der zugrunde liegenden ThreadPoolExecutor arbeiten, indem er sie instanziiert und die gewünschte Konfiguration übergibt dazu.

72
harsh

dann wird das einen Unterschied machen?

Es wird Ihren Code für wenig Nutzen komplizierter machen.

Ich versuche zu verstehen, was der Unterschied zwischen meinem ursprünglichen Code, der ExecutorService verwendet, und dem neuen Code ist, den ich mit ThreadPoolExectuor eingefügt habe.

So gut wie nichts. Executors erstellt einen ThreadPoolExecutor für die eigentliche Arbeit.

Einige meiner Teamkollegen sagten, der zweite (ThreadPoolExecutor) sei der richtige Weg?

Nur weil es komplizierter ist, heißt das noch lange nicht, dass es das Richtige ist. Die Designer stellten die Executors.newXxxx-Methoden zur Verfügung, um Ihnen das Leben zu vereinfachen, und weil sie erwartet hatten, dass Sie diese Methoden verwenden. Ich schlage vor, Sie verwenden sie auch.

23
Peter Lawrey
  1. Executors # newFixedThreadPool (int nThreads)

    ExecutorService executor = Executors.newFixedThreadPool(20);
    

ist im Grunde

 return new ThreadPoolExecutor(20, 20,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());

2.

BlockingQueue<Runnable> threadPool = new LinkedBlockingQueue<Runnable>();
ThreadPoolExecutor tpExecutor = new ThreadPoolExecutor(20, 2000, 0L,
    TimeUnit.MILLISECONDS, threadPool);

Im zweiten Fall erhöhen Sie nur die maxPoolSize auf 2000. Ich bezweifle, dass Sie dies benötigen würden.

11
NINCOMPOOP

Ich glaube, ein weiterer Vorteil ist mit RejectionHandler. Korrigieren Sie mich, wenn Sie falsch liegen

Im ersten Beispiel haben Sie nur 20 Threads mit der folgenden Anweisung erstellt

ExecutorService executor = Executors.newFixedThreadPool(20);

Im zweiten Beispiel haben Sie den Thread-Limits-Bereich zwischen 20 to 2000 Eingestellt.

 ThreadPoolExecutor tpExecutor = new ThreadPoolExecutor(20, 2000, 0L, 
                                     TimeUnit.MILLISECONDS,threadPool);

Weitere Threads stehen zur Bearbeitung zur Verfügung. Sie haben die Aufgabenwarteschlange jedoch als unbegrenzte Warteschlange konfiguriert.

ThreadPoolExecutor wäre effektiver, wenn Sie viele oder alle der folgenden Parameter angepasst hätten.

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

RejectedExecutionHandler ist nützlich, wenn Sie max capacity for workQueue festlegen und die Anzahl der Aufgaben, die an Executor übergeben wurden, die Kapazität von workQueue überschreitet.

Weitere Informationen finden Sie im Abschnitt Abgelehnte Aufgaben in ThreadPoolExecutor .

2
Ravindra babu

Nach 2 Tagen GC out of memory exception, ThreadPoolExecutor hat mir das Leben gerettet. :)

Wie Balaji sagte,

[..] Ein weiterer Vorteil ist mit RejectionHandler.

In meinem Fall hatte ich eine Menge von RejectedExecutionException und die Angabe (wie folgt) der Discard-Richtlinie löste alle meine Probleme.

private ThreadPoolExecutor executor = new ThreadPoolExecutor(1, cpus, 1, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());

Aber sei vorsichtig! Dies funktioniert nur, wenn Sie nicht ​​die Threads ausführen müssen alle, die Sie an den Executor senden.

Weitere Informationen zu ThreadPoolExecutor finden Sie unter Darrens Antwort

1
user5545873