1. ホーム
  2. java

[解決済み] サイズ制限のあるキャッシュスレッドプールの作成は不可能?

2022-06-07 02:31:49

質問

作成できるスレッド数に制限のあるキャッシュスレッドプールを作成することはできないようです。

以下は、静的な Executors.newCachedThreadPool が標準的なJavaライブラリでどのように実装されているかを示します。

 public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

というわけで、このテンプレートを使って、固定サイズのキャッシュされたスレッドプールを作って行くことにします。

new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new SynchronusQueue<Runable>());

さて、これを使って3つのタスクを投入すると、すべてうまくいきます。 それ以上のタスクを投入すると、実行拒否の例外が発生します。

これを試してみてください。

new ThreadPoolExecutor(0, 3, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runable>());

この場合、すべてのスレッドが順次実行されることになります。 つまり、スレッドプールはあなたのタスクを処理するために複数のスレッドを作成することはありません。

の execute メソッドにあるバグです。 ThreadPoolExecutor ? それとも、これは意図的なものなのでしょうか? それとも他に方法があるのでしょうか?

編集: 私はキャッシュされたスレッドプールのようなもの (必要に応じてスレッドを作成し、あるタイムアウトの後にそれらを終了させる) が欲しいのですが、作成できるスレッドの数に制限があり、スレッドの制限に達した後に追加のタスクをキューに入れ続ける機能があります。 sjleeの回答によると、これは不可能だそうです。 を見ると execute() メソッドを見ると ThreadPoolExecutor を使用することは、確かに不可能です。 私はサブクラスで ThreadPoolExecutor をオーバーライドして execute() のように SwingWorker がそうであるように、しかし SwingWorker はその execute() は完全にハックされています。

どのように解決するのですか?

この ThreadPoolExecutor には以下のようないくつかの重要な動作があり、あなたの問題はこれらの動作で説明することができます。

タスクが送信されたとき。

  1. スレッドプールがコアサイズに達していない場合、新しいスレッドを作成します。
  2. コアサイズに達しており、アイドルスレッドがない場合、タスクをキューに入れます。
  3. コアサイズに達し、アイドルスレッドがなく、キューが一杯になった場合、新しいスレッドを作成します(最大サイズに達するまで)。
  4. 最大サイズに達し、アイドルスレッドがなく、キューが一杯になった場合、拒否ポリシーが起動します。

最初の例では SynchronousQueue は本質的に 0 のサイズを持っています。したがって、最大サイズ (3) に達した瞬間に拒否ポリシーが作動します (#4)。

2 番目の例では、選択されたキューは LinkedBlockingQueue であり、サイズは無制限です。 したがって、2番目の動作で行き詰まります。

キャッシュ型や固定型は挙動がほぼ決まっているため、あまりいじることができません。

もし、制限された動的なスレッドプールを持ちたいのであれば、有限サイズのキューと組み合わせた、正のコアサイズと最大サイズを使用する必要があります。 たとえば

new ThreadPoolExecutor(10, // core size
    50, // max size
    10*60, // idle timeout
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<Runnable>(20)); // queue with a size

追記 : これはかなり古い回答で、コアサイズ0に関してはJDKが動作を変更したようです。 JDK1.6以降、コアサイズが0でプールにスレッドがない場合、ThreadPoolExecutorはそのタスクを実行するためにスレッドを追加することになります。したがって、コアサイズ0は上記のルールの例外となります。感謝 スティーブ に対して 持ってくる を知らせてくれてありがとう。