web-dev-qa-db-ja.com

LinkedBlockingQueueを使用したExecutorServiceとThreadPoolExecutor

負荷とパフォーマンスのテストを行っているときに、クライアントコードのエンドツーエンドのパフォーマンスを測定するために複数のスレッドを生成する必要があるマルチスレッドプロジェクトに取り組んでいます。そこで、ExecutorServiceを使用する以下のコードを作成しました。

以下は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
    }   
}

問題の説明:

今、私はインターネットでいくつかの記事を読んでいた。 ThreadPoolExecutorもあることがわかりました。だから私はどちらを使うべきか混乱してしまいました。

上記のコードを次の場所から置き換えた場合:

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

に:

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());
    }

これは何か違いがありますか? ExecutorServiceを使用した元のコードとThreadPoolExecutorを使用して貼り付けた新しいコードの違いを理解しようとしています。私のチームの仲間の何人かは、2番目の(ThreadPoolExecutor)が正しい使用方法だと言いました。

誰も私のためにこれを明確にすることはできますか?

48
user1813228

Executors.newFixedThreadPoolのソースは次のとおりです。

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

上記のように、デフォルトの構成でThreadPoolExecutorクラスを内部的に使用します。現在、デフォルト設定がLinkedBlockingQueueの代わりに優先キューを使用する必要があるなどとは言えないシナリオがあります。そのような場合、呼び出し元はインスタンス化して必要な設定を渡すことで、基になるThreadPoolExecutorを直接操作できます。

72
harsh

それは違いを生むでしょうか?

コードをより複雑にしますが、ほとんどメリットはありません。

ExecutorServiceを使用している元のコードとThreadPoolExectuorを使用している貼り付けた新しいコードの違いを理解しようとしていますか?

何もない。 Executorsは、実際の作業を行うためにThreadPoolExecutorを作成します。

私のチームメイトの何人かは、2番目の(ThreadPoolExecutor)を使用する正しい方法だと言いましたか?

より複雑だからといって、それが正しいことだとは限りません。設計者は、Executors.newXxxxメソッドを提供して、あなたがより簡単に生活できるようにしました。彼らは、それらのメソッドを使用することを期待していたからです。それらも使用することをお勧めします。

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

    ExecutorService executor = Executors.newFixedThreadPool(20);
    

基本的に

 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);

2番目のケースでは、maxPoolSizeを2000に増やしているだけです。

11
NINCOMPOOP

もう1つの利点はRejectionHandlerを使用することです。間違っている場合は修正してください

最初の例では、次のステートメントで20個のスレッドを作成しました。

ExecutorService executor = Executors.newFixedThreadPool(20);

2番目の例では、20 to 2000の間にスレッド制限範囲を設定しました

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

他のスレッドも処理に使用できます。ただし、タスクキューを無制限のキューとして設定しました。

ThreadPoolExecutor は、以下のパラメーターの多くまたはすべてをカスタマイズした場合により効果的です。

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

RejectedExecutionHandlerは、max capacity for workQueueを設定し、Executorに送信されたタスクの数がworkQueue容量を超える場合に役立ちます。

詳細については、 ThreadPoolExecutor の「拒否されたタスク」セクションをご覧ください。

2
Ravindra babu

GC out of memory exceptionの2日後、ThreadPoolExecutorが私の命を救いました。 :)

バラジが言ったように、

[..]もう1つの利点は、RejectionHandlerを使用することです。

私の場合、RejectedExecutionExceptionがたくさんあり、(次のように)破棄ポリシーを指定すると、すべての問題が解決しました。

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

しかし、注意してください! do n'tスレッドを実行する必要があるall場合にのみ機能しますエグゼキューターに送信すること。

ThreadPoolExecutorの詳細については、 ダレンの答え をご覧ください。

1
user5545873