137

作成できるスレッドの数に制限があるキャッシュされたスレッド プールを作成することは不可能のようです。

Executors.newCachedThreadPool以下は、標準 Java ライブラリでのstatic の実装方法です。

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

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

これはThreadPoolExecutor?の execute メソッドのバグです。それとも、これは意図的なものですか?それとも何か他の方法がありますか?

編集:キャッシュされたスレッドプールとまったく同じものが必要です(オンデマンドでスレッドを作成し、タイムアウト後にそれらを強制終了します)が、作成できるスレッドの数に制限があり、追加のタスクをキューに入れ続ける機能が必要ですスレッド制限に達しました。sjlee の回答によると、これは不可能です。execute()その方法を見ると、ThreadPoolExecutorさすがに無理です。ThreadPoolExecutorサブクラス化してオーバーライドする必要がありexecute()ますSwingWorkerSwingWorker、その中で行うことexecute()は完全なハックです。

4

14 に答える 14

243

にはThreadPoolExecutor次のいくつかの重要な動作があり、問題はこれらの動作によって説明できます。

タスクが送信されると、

  1. スレッド プールがコア サイズに達していない場合は、新しいスレッドが作成されます。
  2. コア サイズに達し、アイドル スレッドがない場合は、タスクをキューに入れます。
  3. コア サイズに達し、アイドル スレッドがなく、キューがいっぱいになると、新しいスレッドが作成されます (最大サイズに達するまで)。
  4. 最大サイズに達し、アイドル状態のスレッドがなく、キューがいっぱいになると、拒否ポリシーが開始されます。

最初の例では、 のサイズは基本的に 0 であることに注意してください。SynchronousQueueしたがって、最大サイズ (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 の動作が変更されたようです。JDK 1.6 以降、コア サイズが 0 でプールにスレッドがない場合、ThreadPoolExecutor はそのタスクを実行するスレッド。したがって、コア サイズ 0 は上記のルールの例外です。私の注意を引いてくれてありがとう、スティーブ

于 2009-11-25T23:11:00.500 に答える
64

私が何かを見逃していない限り、元の質問に対する解決策は簡単です。次のコードは、元のポスターで説明されているように、目的の動作を実装しています。無制限のキューで動作する最大 5 つのスレッドを生成し、アイドル状態のスレッドは 60 秒後に終了します。

tp = new ThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>());
tp.allowCoreThreadTimeOut(true);
于 2011-11-14T17:24:56.290 に答える
7

AbortPolicy最初の例では、がデフォルトであるため、後続のタスクは拒否されますRejectedExecutionHandler。ThreadPoolExecutorには、次のポリシーが含まれています。これらのポリシーは、setRejectedExecutionHandlerメソッドを介して変更できます。

CallerRunsPolicy
AbortPolicy
DiscardPolicy
DiscardOldestPolicy

CallerRunsPolicyでキャッシュされたスレッドプールが必要なようです。

于 2009-11-25T22:36:54.750 に答える
5

ここでの回答のどれも、Apache の HTTP クライアント (3.x バージョン) を使用して限られた量の HTTP 接続を作成することに関係していた私の問題を解決しませんでした。適切なセットアップを見つけるのに数時間かかったので、共有します。

private ExecutorService executor = new ThreadPoolExecutor(5, 10, 60L,
  TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
  Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());

これにより、ThreadPoolExecutor5 から始まり、実行に使用する最大 10 個の同時実行スレッドを保持する が作成されCallerRunsPolicyます。

于 2010-12-05T01:01:38.913 に答える
3

ThreadPoolExecutor の Javadoc によると:

実行中のスレッドが corePoolSize より多く、 maximumPoolSize より少ない場合、キューがいっぱいの場合にのみ新しいスレッドが作成されます。corePoolSize と maximumPoolSize を同じに設定すると、固定サイズのスレッド プールが作成されます。

(私のものを強調してください。)

ジッターの答えはあなたが望むものですが、私のものはあなたの他の質問に答えます。:)

于 2009-11-25T22:30:04.973 に答える
2

多くのメソッド/プロパティがプライベートであるため、PooledExecutorServiceからサブクラス化した場合でも、回答のいずれかが実際に質問に答えているようには見えません-実際、これを行う方法はわかりません-たとえば、addIfUnderMaximumPoolSizeを保護することができます以下をせよ:

class MyThreadPoolService extends ThreadPoolService {
    public void execute(Runnable run) {
        if (poolSize() == 0) {
            if (addIfUnderMaximumPoolSize(run) != null)
                return;
        }
        super.execute(run);
    }
}

私が得た最も近いものはこれでした-しかしそれでもあまり良い解決策ではありません

new ThreadPoolExecutor(min, max, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>()) {
    public void execute(Runnable command) {
        if (getPoolSize() == 0 && getActiveCount() < getMaximumPoolSize()) {        
            super.setCorePoolSize(super.getCorePoolSize() + 1);
        }
        super.execute(command);
    }

    protected void afterExecute(Runnable r, Throwable t) {
         // nothing in the queue
         if (getQueue().isEmpty() && getPoolSize() > min) {
             setCorePoolSize(getCorePoolSize() - 1);
         }
    };
 };

psは上記をテストしていません

于 2011-10-14T23:39:18.673 に答える
2

ここに別の解決策があります。このソリューションは、あなたが望むように動作すると思います(ただし、このソリューションを誇りに思っているわけではありません):

final LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>() {
    public boolean offer(Runnable o) {
        if (size() > 1)
            return false;
        return super.offer(o);
    };

    public boolean add(Runnable o) {
        if (super.offer(o))
            return true;
        else
            throw new IllegalStateException("Queue full");
    }
};

RejectedExecutionHandler handler = new RejectedExecutionHandler() {         
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        queue.add(r);
    }
};

dbThreadExecutor =
        new ThreadPoolExecutor(min, max, 60L, TimeUnit.SECONDS, queue, handler);
于 2011-10-15T01:18:23.720 に答える
2

これはあなたが望むものです(少なくとも私はそう思います)。説明については、ジョナサン・ファインバーグの回答を確認してください

Executors.newFixedThreadPool(int n)

無制限の共有キューで動作する固定数のスレッドを再利用するスレッド プールを作成します。どの時点でも、最大で nThreads スレッドがアクティブな処理タスクになります。すべてのスレッドがアクティブなときに追加のタスクが送信されると、それらのタスクは、スレッドが使用可能になるまでキューで待機します。シャットダウン前の実行中に障害が発生していずれかのスレッドが終了した場合、後続のタスクを実行する必要がある場合は、新しいスレッドが代わりに使用されます。プール内のスレッドは、明示的にシャットダウンされるまで存在します。

于 2009-11-25T22:23:20.827 に答える
2

もう1つのオプションがあります。new SynchronousQueue を使用する代わりに、他のキューも使用できますが、そのサイズが 1 であることを確認する必要があるため、executorservice は新しいスレッドを作成する必要があります。

于 2011-08-29T10:10:46.020 に答える
0

これは Java8+ (およびその他、今のところ..) で機能します。

     Executor executor = new ThreadPoolExecutor(3, 3, 5, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>()){{allowCoreThreadTimeOut(true);}};

ここで、3 はスレッド数の制限、5 はアイドル スレッドのタイムアウトです。

自分で機能するかどうかを確認したい場合は、次のコードで作業を行います。

public static void main(String[] args) throws InterruptedException {
    final int DESIRED_NUMBER_OF_THREADS=3; // limit of number of Threads for the task at a time
    final int DESIRED_THREAD_IDLE_DEATH_TIMEOUT=5; //any idle Thread ends if it remains idle for X seconds

    System.out.println( java.lang.Thread.activeCount() + " threads");
    Executor executor = new ThreadPoolExecutor(DESIRED_NUMBER_OF_THREADS, DESIRED_NUMBER_OF_THREADS, DESIRED_THREAD_IDLE_DEATH_TIMEOUT, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>()) {{allowCoreThreadTimeOut(true);}};

    System.out.println(java.lang.Thread.activeCount() + " threads");

    for (int i = 0; i < 5; i++) {
        final int fi = i;
        executor.execute(() -> waitsout("starting hard thread computation " + fi, "hard thread computation done " + fi,2000));
    }
    System.out.println("If this is UP, it works");

    while (true) {
        System.out.println(
                java.lang.Thread.activeCount() + " threads");
        Thread.sleep(700);
    }

}

static void waitsout(String pre, String post, int timeout) {
    try {
        System.out.println(pre);
        Thread.sleep(timeout);
        System.out.println(post);
    } catch (Exception e) {
    }
}

上記のコードの出力は

1 threads
1 threads
If this is UP, it works
starting hard thread computation 0
4 threads
starting hard thread computation 2
starting hard thread computation 1
4 threads
4 threads
hard thread computation done 2
hard thread computation done 0
hard thread computation done 1
starting hard thread computation 3
starting hard thread computation 4
4 threads
4 threads
4 threads
hard thread computation done 3
hard thread computation done 4
4 threads
4 threads
4 threads
4 threads
3 threads
3 threads
3 threads
1 threads
1 threads
于 2019-10-23T13:18:42.637 に答える