私は以下を実装する最も簡単で最も簡単な方法を探しています:
n
タスクのみです。n
に達すると、実行中のスレッドの数がn
を下回るまで、ワーカーは開始されません。Executors.newFixedThreadPool はあなたの要件に合うと思います。メインスレッドに結果を返すかどうか、タスクが完全に自己完結型かどうか、および事前に実行するタスクのコレクションがあるかどうかに応じて、結果のExecutorServiceを使用するさまざまな方法があります。タスクが何らかのイベントに応答してキューに入れられるかどうか。
_ Collection<YourTask> tasks = new ArrayList<YourTask>();
YourTask yt1 = new YourTask();
...
tasks.add(yt1);
...
ExecutorService exec = Executors.newFixedThreadPool(5);
List<Future<YourResultType>> results = exec.invokeAll(tasks);
_
あるいは、何らかのイベントに応答して実行する新しい非同期タスクがある場合は、ExecutorServiceの単純なexecute(Runnable)
メソッドを使用することをお勧めします。
/* Get an executor service that will run a maximum of 5 threads at a time: */
ExecutorService exec = Executors.newFixedThreadPool(5);
/* For all the 100 tasks to be done altogether... */
for (int i = 0; i < 100; i++) {
/* ...execute the task to run concurrently as a runnable: */
exec.execute(new Runnable() {
public void run() {
/* do the work to be done in its own thread */
System.out.println("Running in: " + Thread.currentThread());
}
});
}
/* Tell the executor that after these 100 steps above, we will be done: */
exec.shutdown();
try {
/* The tasks are now running concurrently. We wait until all work is done,
* with a timeout of 50 seconds: */
boolean b = exec.awaitTermination(50, TimeUnit.SECONDS);
/* If the execution timed out, false is returned: */
System.out.println("All done: " + b);
} catch (InterruptedException e) { e.printStackTrace(); }
Executors.newFixedThreadPool(int)
Executor executor = Executors.newFixedThreadPool(n);
Runnable runnable = new Runnable() {
public void run() {
// do your thing here
}
}
executor.execute(runnable);
Executorフレームワークを使用します。つまり newFixedThreadPool(N)
タスクキューが制限されず、タスクがより短い時間間隔で完了する場合は、Executors.newFixedThreadPool(n)
;を使用できます。専門家が示唆するように。
このソリューションの唯一の欠点は、無制限のタスクキューサイズです。あなたはそれを制御できません。タスクキューの大量の蓄積は、アプリケーションのパフォーマンスを低下させ、一部のシナリオではメモリ不足を引き起こす可能性があります。
ExecutorService
を使用して有効にする場合work stealing
アイドルワーカースレッドが、タスクキュー内のタスクを盗むことにより、ビジーなワーカースレッドからの作業負荷を共有するメカニズム。これは、Executor ServiceのForkJoinPoolタイプを返します。
public static
ExecutorService newWorkStealingPool
(int並列処理)指定された並列処理レベルをサポートするのに十分なスレッドを維持するスレッドプールを作成し、複数のキューを使用して競合を減らします。並列処理レベルは、タスク処理に積極的に関与している、または関与できるスレッドの最大数に対応します。スレッドの実際の数は動的に増減します。ワークスチールプールは、送信されたタスクが実行される順序を保証しません。
フロータスクの実行を制御する多くのパラメーターを制御するAPIには柔軟性があるため、ThreadPoolExecutor
を好みます。
ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
あなたの場合、両方を設定してくださいcorePoolSize and maximumPoolSize as N
。ここでは、タスクキューのサイズを制御し、独自のカスタムスレッドファクトリと拒否ハンドラーポリシーを定義できます。
プールサイズを動的に制御するための関連するSEの質問を見てください。
自分でロールしたい場合:
private static final int MAX_WORKERS = n;
private List<Worker> workers = new ArrayList<Worker>(MAX_WORKERS);
private boolean roomLeft() {
synchronized (workers) {
return (workers.size() < MAX_WORKERS);
}
}
private void addWorker() {
synchronized (workers) {
workers.add(new Worker(this));
}
}
public void removeWorker(Worker worker) {
synchronized (workers) {
workers.remove(worker);
}
}
public Example() {
while (true) {
if (roomLeft()) {
addWorker();
}
}
}
Workerは、Threadを拡張するクラスです。各ワーカーは、このクラスのremoveWorkerメソッドを呼び出し、処理が完了すると、自分自身をパラメーターとして渡します。
そうは言っても、Executorフレームワークはずっと良く見えます。
編集:誰かがこれをなぜダウンモッド化するのではなく、なぜそれほど悪いのか説明したいと思いませんか?
ここの他の人が述べたように、あなたの最善の策は Executors クラスでスレッドプールを作ることです:
ただし、独自のコードを作成したい場合は、このコードにより、続行方法がわかります。基本的に、すべての新しいスレッドをスレッドグループに追加し、グループ内にN個を超えるアクティブスレッドが存在しないようにします。
Task[] tasks = getTasks(); // array of tasks to complete
ThreadGroup group = new ThreadGroup();
int i=0;
while( i<tasks.length || group.activeCount()>0 ) {
if( group.activeCount()<N && i<tasks.length ) {
new TaskThread(group, tasks[i]).start();
i++;
} else {
Thread.sleep(100);
}
}