web-dev-qa-db-ja.com

ThreadPoolExecutor:タスクがキューに入れられ、送信されません

ThreadPoolExecutorに送信されたタスクが長時間実行されているシナリオがあります。スレッドプールが開始されると、コアプールサイズ= 5、最大プールサイズ= 20、キューサイズ10で開始します。このアプリケーションでは、約10個のタスクが送信されます。ほとんどの場合、これらのタスクは数分/時間実行されてから完了します。ただし、5つのタスクすべてがI/Oでハングする状況がありました。その結果、コアプールのサイズは最大に達しましたが、Threadpoolexecutorキューがいっぱいではありませんでした。したがって、追加の5つのタスクを実行する機会はありませんでした。そのようなシナリオをどのように処理できるかを提案してください。そのような状況では、キューを小さくする方が良いオプションですか? threadPoolを初期化する際の最適なキューサイズはどれくらいですか?

また、ハングしたタスクに関して、スレッドプールからスレッドを引き出す方法はありますか?その場合、少なくとも他のタスクを実行する機会が得られます。

10
user1269597

全体的な状況は次のようになります。

core pool size = 5,
max pool size = 20 and 
queue size of 10

10個のタスクが送信されます。そのうち

  1. 5 I/Oでハングしたタスク=>コアプールサイズのすべてのスレッドが占有されています。したがって、アイドル状態のスレッドはありません。
  2. 5タスクが残っています。アイドル状態のスレッドがなく、キューは10個のタスクに対応できるため、これらの5つのスレッドはキューに入れられます。これらのエンキューされたタスクは、キューがいっぱいになるか、コアプール内のスレッドのいずれかが解放されるまで実行されません。

したがって、あなたのプログラムはハングします。

ThreadPoolExecutor watch here のダイナミクスについて詳しく知るには。このドキュメントの注目すべき点は次のとおりです。

  • 実行中のスレッドがcorePoolSize未満の場合、エグゼキューターは常にキューイングよりも新しいスレッドの追加を優先します。
  • CorePoolSize以上のスレッドが実行されている場合、Executorは常に、新しいスレッドを追加するのではなく、リクエストをキューに入れることを優先します。
  • リクエストをキューに入れることができない場合、maximumPoolSizeを超えない限り、新しいスレッドが作成されます。超えた場合、タスクは拒否されます。

[〜#〜]編集[〜#〜]
コアプールサイズを増やしたい場合は、setCorePoolSize(int corePoolSize)を使用できます。 corepoolsizeを増やすと、必要に応じて、キューに入れられたタスクを実行するために新しいスレッドが開始されます。

8
Vishal K

ThreadPoolExecutorのJavadoc 状態:

送信されたタスクを転送および保持するために、任意のBlockingQueueを使用できます。このキューの使用は、プールのサイズ設定と相互作用します。

  • 実行中のスレッドがcorePoolSize未満の場合、エグゼキューターは常にキューイングよりも新しいスレッドの追加を優先します。
  • CorePoolSize以上のスレッドが実行されている場合、Executorは常に、新しいスレッドを追加するのではなく、リクエストをキューに入れることを優先します。
  • リクエストをキューに入れることができない場合、maximumPoolSizeを超えない限り、新しいスレッドが作成されます。超えた場合、タスクは拒否されます。

5つのスレッドが「ハング」した後でキューサイズを超えない限り、それ以上のスレッドを取得することはありません。

本当の答えは、スレッドがハングする原因となっている問題を修正することです。それ以外の場合は、submit()によって返されるFuturesを使用して、スレッドの実行時間が長すぎる場合にスレッドをキャンセルするスキームを実装する必要があります。

8
Brian Roach

別のアプローチは、キューで待機しているタスクの数に基づいてcorePoolSizeを設定することだと思います。 setCorePoolSizeを使用してcorePoolSizeを制御できます。サンプルモニタースレッドは、threadPoolExecutorを制御できます。このモニターを改善して、並列度を調整することもできます。

    public class ExecutorMonitor extends Thread{

            ThreadPoolExecutor executor = null;
            int initialCorePoolSize;
            public ExecutorMonitor(ThreadPoolExecutor executor)
            {
                this.executor = executor;
                this.initialCorePoolSize = executor.getCorePoolSize();
            }
            @Override
            public void run()
            {
                while (true)
                {   
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (executor.getQueue().size() > 0)
                    {
                        if(executor.getActiveCount() < executor.getMaximumPoolSize())
                            executor.setCorePoolSize(executor.getCorePoolSize() + 1);
                    }
                    if (executor.getQueue().size() == 0)
                    {
                        if(executor.getCorePoolSize() > initialCorePoolSize)
                            executor.setCorePoolSize(executor.getCorePoolSize() -1);
                    }
                }
            }
        }
1
celik

Jdkの「可変」サイズのスレッドプール機能は少し注意が必要です。基本的に、使用しているセットアップは、概説した理由によりうまく機能しません。可変サイズのスレッドプールが必要な場合、通常は次の設定を使用します。

   ThreadPoolExecutor executor = new ThreadPoolExecutor(maxPoolSize, maxPoolSize,
                                                         DEFAULT_THREAD_TIMEOUT, DEFAULT_THREAD_TIMEOUT_UNIT,
                                                         new LinkedBlockingQueue<Runnable>(),
                                                         threadFactory);
    executor.allowCoreThreadTimeOut(true);

これにより、1スレッドとmaxPoolSizeスレッドの間で変化する可変サイズのスレッドプールが作成されます。コアサイズは最大サイズと同じであるため、プールは常にキューイングよりもスレッドの追加を優先します。したがって、スレッド数が最大になるまでキューをバックアップすることはありません。

更新:ハングタスクの問題については、タスクの長さに上限がある場合、別のマネージャーに未処理のFutureを追跡させ、最大実行時間を超えている場合はキャンセルすることができます。もちろん、これはタスクが中断可能であることを前提としています。

0
jtahlborn

基本的な問題は、ユーザーが実際に無制限のキューシナリオでCORE_POOL_SIZEスレッドを取得していることです。そのため、コアプールに5つのスレッドがあり、それが彼が使用できるすべてである場合、最大サイズは何の役にも立ちません。すべての場合にスレッド実行の時間を短縮することをお勧めしますが、本番シナリオでは、サードパーティサービスの動作を制御できないことが多いため、解決策はコアプールサイズを最大プールサイズと等しくするか、キューサイズを制限することです。 。