69

ThreadPoolExecutor を使用して多くのタスクを実行しようとしています。以下は架空の例です。

def workQueue = new ArrayBlockingQueue<Runnable>(3, false)
def threadPoolExecutor = new ThreadPoolExecutor(3, 3, 1L, TimeUnit.HOURS, workQueue)
for(int i = 0; i < 100000; i++)
    threadPoolExecutor.execute(runnable)

java.util.concurrent.RejectedExecutionException問題は、タスクの数が作業キューのサイズを超えているため、すぐに取得できることです。ただし、私が探している望ましい動作は、キューに空きができるまでメイン スレッドをブロックすることです。これを達成するための最良の方法は何ですか?

4

8 に答える 8

76

非常に狭い状況では、必要なことを行う java.util.concurrent.RejectedExecutionHandler を実装できます。

RejectedExecutionHandler block = new RejectedExecutionHandler() {
  rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
     executor.getQueue().put( r );
  }
};

ThreadPoolExecutor pool = new ...
pool.setRejectedExecutionHandler(block);

今。これは、次の理由から非常に悪い考えです。

  • キューに入れられたものが表示される前に、プール内のすべてのスレッドが終了する可能性があるため、デッドロックが発生しやすくなります。適切なキープアライブ時間を設定して、これを軽減します。
  • Executor が期待する方法でタスクがラップされていません。多くのエグゼキュータ実装は、実行前にある種の追跡オブジェクトでタスクをラップします。あなたのソースを見てください。
  • getQueue() による追加は、API によって強く推奨されておらず、ある時点で禁止される可能性があります。

ほぼ常に優れた戦略は、execute() を呼び出しているスレッドでタスクを実行することによってアプリを調整する ThreadPoolExecutor.CallerRunsPolicy をインストールすることです。

ただし、すべての固有のリスクを伴うブロッキング戦略が本当に必要な場合もあります。私はこれらの条件の下で言うだろう

  • execute() を呼び出すスレッドは 1 つだけです。
  • キューの長さを非常に短くする必要がある (またはしたい)
  • この作業を実行するスレッドの数を絶対に制限する必要があります (通常は外部の理由により)。呼び出し元実行戦略はそれを破ります。
  • タスクのサイズは予測できないため、プールが 4 つの短いタスクで一時的にビジー状態になり、execute を呼び出す 1 つのスレッドが大きなタスクでスタックした場合、呼び出し元の実行によって飢餓が発生する可能性があります。

だから、私が言うように。必要になることはめったになく、危険な場合もありますが、これで終わりです。

幸運を。

于 2010-08-19T03:47:45.967 に答える