One possibility might be to use the standard ThreadPoolExecutor with another task queue task.
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; } } }
The idea here is that when you have a new assignment, you call
taskRunner.runTasks(jobTask1, jobTask2, jobTask3);
and he will queue the tasks in such a way that they interspersed well with any existing tasks in the queue (if any). Suppose you have one task, the tasks of which have priority numbers j 1 t 1 = 3, j 1 t 2 = 103 and j 1 t 3 = 203. In the absence of other tasks, these tasks will be performed one after another as quickly as possible. But if you send another task with your three tasks, they will be assigned priority numbers j 2 t 1 = 4, j 2 t 2 = 104 and j 2 t 3 = 204, which means that the queue now looks like
j 1 t 1 , j 2 t 1 , j 1 t 2 , j 2 t 2 , etc.
This is not ideal, because if all the threads are currently running (in tasks from task 1), the first task of task 2 cannot start until one of the tasks of task 1 is completed (if there is no external way for you detect this, interrupt and reinstall some tasks of the 1st task). The easiest way to make things fairer is to break longer tasks into smaller segments and queue them as separate tasks - you need to get to the point where each separate task includes more tasks than there are threads in the pool, so some tasks will always be started in queues, and not assigned directly to threads (if there are idle threads, then exec.execute() passes the task directly to the thread without going through all the queues).
Ian roberts
source share