負荷とパフォーマンスのテストを行っているときに、クライアントコードのエンドツーエンドのパフォーマンスを測定するために複数のスレッドを生成する必要があるマルチスレッドプロジェクトに取り組んでいます。そこで、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)が正しい使用方法だと言いました。
誰も私のためにこれを明確にすることはできますか?
Executors.newFixedThreadPool
のソースは次のとおりです。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
上記のように、デフォルトの構成でThreadPoolExecutor
クラスを内部的に使用します。現在、デフォルト設定がLinkedBlockingQueue
の代わりに優先キューを使用する必要があるなどとは言えないシナリオがあります。そのような場合、呼び出し元はインスタンス化して必要な設定を渡すことで、基になるThreadPoolExecutor
を直接操作できます。
それは違いを生むでしょうか?
コードをより複雑にしますが、ほとんどメリットはありません。
ExecutorServiceを使用している元のコードとThreadPoolExectuorを使用している貼り付けた新しいコードの違いを理解しようとしていますか?
何もない。 Executors
は、実際の作業を行うためにThreadPoolExecutorを作成します。
私のチームメイトの何人かは、2番目の(ThreadPoolExecutor)を使用する正しい方法だと言いましたか?
より複雑だからといって、それが正しいことだとは限りません。設計者は、Executors.newXxxxメソッドを提供して、あなたがより簡単に生活できるようにしました。彼らは、それらのメソッドを使用することを期待していたからです。それらも使用することをお勧めします。
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に増やしているだけです。
もう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 の「拒否されたタスク」セクションをご覧ください。
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
の詳細については、 ダレンの答え をご覧ください。