一种可能是使用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);
并且它将以这样一种方式将任务排队,使它们与队列中的任何现有任务(如果有的话)很好地交错。假设您有一个作业排队,其任务的优先级编号为 j 1 t 1 =3、j 1 t 2 =103 和 j 1 t 3 =203。在没有其他作业的情况下,这些任务会以最快的速度一个接一个地执行。但是,如果您提交另一个包含三个任务的作业,这些任务将被分配优先级编号 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 2 t 2等。
然而,这并不完美,因为如果所有线程当前都在工作(来自工作 1 的任务),那么工作 2 的第一个任务在其中一个工作 1 任务完成之前无法启动(除非有一些外部方法可供您检测这并中断并重新排队作业 1 的一些任务)。使事情更公平的最简单方法是将运行时间较长的任务分解为更小的部分并将它们作为单独的任务排队 - 您需要达到一个点,即每个单独的作业涉及的任务多于池中的线程数,这样某些任务将始终在队列中开始,而不是直接分配给线程(如果有空闲线程,则将exec.execute()
任务直接传递给线程,而根本不经过队列)。