1つの可能性はThreadPoolExecutor
、異なる種類のタスクキューで標準を使用することです。
public class TaskRunner {
private static class PriorityRunnable implements Runnable,
Comparable<PriorityRunnable> {
private Runnable theRunnable;
private int priority = 0;
public PriorityRunnable(Runnable r, int priority) {
this.theRunnable = r;
this.priority = priority;
}
public int getPriority() {
return priority;
}
public void run() {
theRunnable.run();
}
public int compareTo(PriorityRunnable that) {
return this.priority - that.priority;
}
}
private BlockingQueue<Runnable> taskQueue = new PriorityBlockingQueue<Runnable>();
private ThreadPoolExecutor exec = new ThreadPoolExecutor(8, 8, 0L,
TimeUnit.MILLISECONDS, taskQueue);
public void runTasks(Runnable... tasks) {
int priority = 0;
Runnable nextTask = taskQueue.peek();
if(nextTask instanceof PriorityRunnable) {
priority = ((PriorityRunnable)nextTask).getPriority() + 1;
}
for(Runnable t : tasks) {
exec.execute(new PriorityRunnable(t, priority));
priority += 100;
}
}
}
ここでの考え方は、新しい仕事があるときに電話をかけるということです
taskRunner.runTasks(jobTask1, jobTask2, jobTask3);
そして、キュー内の既存のタスク(存在する場合)とうまくインターリーブするようにタスクをキューに入れます。1つのジョブがキューに入れられ、そのタスクの優先順位番号がj 1 t 1 = 3、j 1 t 2 = 103、およびj 1 t 3 =203であるとします。他のジョブがない場合、これらのタスクは可能な限り迅速に次々に実行されます。ただし、独自の3つのタスクを持つ別のジョブを送信すると、これらには優先順位番号j 2 t 1 = 4、j 2 t 2 = 104、およびj 2 t 3 = 204が割り当てられます。つまり、キューは次のようになります。
j 1 t 1、j 2 t 1、j 1 t 2、j 2t2など。
ただし、これは完全ではありません。すべてのスレッドが現在(ジョブ1のタスクで)動作している場合、ジョブ1のタスクの1つが完了するまで、ジョブ2の最初のタスクを開始できません(外部で検出する方法がない限り)。これと、ジョブ1のタスクの一部を中断して再キューイングします)。物事をより公平にする最も簡単な方法は、実行時間の長いタスクを小さなセグメントに分割し、それらを個別のタスクとしてキューに入れることです。個々のジョブには、プール内のスレッドよりも多くのタスクが含まれるようにする必要があります。一部のタスクは、スレッドに直接割り当てられるのではなく、常にキューで開始されるようにします(アイドル状態のスレッドがある場合はexec.execute()
、キューをまったく通過せずに、タスクをスレッドに直接渡します)。