How to implement PriorityBlockingQueue with ThreadPoolExecutor and custom tasks - java

I've searched a lot but could not find a solutuion to my problem.
I have my own class, BaseTask, that uses a ThreadPoolExecutor to handle tasks. I want task prioritization, but when I try to use a PriorityBlockingQueue I get ClassCastException because the ThreadPoolExecutor wraps my Tasks into a FutureTask object.
This obviously makes sense because the FutureTask does not implement Comparable, but how would I go on to solve the priority problem? I've read that you could override newTaskFor() in ThreadPoolExecutor, but I can not seem to find this method at all...?
Any suggestions would be much appreciated!
Some code to help:
In my BaseTask class I have
private static final BlockingQueue<Runnable> sWorkQueue = new PriorityBlockingQueue<Runnable>();
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BaseThreadPoolExecutor sExecutor = new BaseThreadPoolExecutor(
1, Integer.MAX_VALUE, 10, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
private final BaseFutureTask<Result> mFuture;
public BaseTask(int priority) {
mFuture = new BaseFutureTask<Result>(mWorker, priority);
}
public final BaseTask<Params, Progress, Result> execute(Params... params) {
/* Some unimportant code here */
sExecutor.execute(mFuture);
}
In BaseFutureTask class
#Override
public int compareTo(BaseFutureTask another) {
long diff = this.priority - another.priority;
return Long.signum(diff);
}
In BaseThreadPoolExecutor class i override the 3 submit methods... The constructor in this class gets called, but none of the submit methods

public class ExecutorPriority {
public static void main(String[] args) {
PriorityBlockingQueue<Runnable> pq = new PriorityBlockingQueue<Runnable>(20, new ComparePriority());
Executor exe = new ThreadPoolExecutor(1, 2, 10, TimeUnit.SECONDS, pq);
exe.execute(new RunWithPriority(2) {
#Override
public void run() {
System.out.println(this.getPriority() + " started");
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
Logger.getLogger(ExecutorPriority.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println(this.getPriority() + " finished");
}
});
exe.execute(new RunWithPriority(10) {
#Override
public void run() {
System.out.println(this.getPriority() + " started");
try {
Thread.sleep(3000);
} catch (InterruptedException ex) {
Logger.getLogger(ExecutorPriority.class.getName()).log(Level.SEVERE, null, ex);
}
System.out.println(this.getPriority() + " finished");
}
});
}
private static class ComparePriority<T extends RunWithPriority> implements Comparator<T> {
#Override
public int compare(T o1, T o2) {
return o1.getPriority().compareTo(o2.getPriority());
}
}
}
as you can guess RunWithPriority is an abstract class that is Runnable and has a Integer priority field

You can use these helper classes:
public class PriorityFuture<T> implements RunnableFuture<T> {
private RunnableFuture<T> src;
private int priority;
public PriorityFuture(RunnableFuture<T> other, int priority) {
this.src = other;
this.priority = priority;
}
public int getPriority() {
return priority;
}
public boolean cancel(boolean mayInterruptIfRunning) {
return src.cancel(mayInterruptIfRunning);
}
public boolean isCancelled() {
return src.isCancelled();
}
public boolean isDone() {
return src.isDone();
}
public T get() throws InterruptedException, ExecutionException {
return src.get();
}
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return src.get();
}
public void run() {
src.run();
}
public static Comparator<Runnable> COMP = new Comparator<Runnable>() {
public int compare(Runnable o1, Runnable o2) {
if (o1 == null && o2 == null)
return 0;
else if (o1 == null)
return -1;
else if (o2 == null)
return 1;
else {
int p1 = ((PriorityFuture<?>) o1).getPriority();
int p2 = ((PriorityFuture<?>) o2).getPriority();
return p1 > p2 ? 1 : (p1 == p2 ? 0 : -1);
}
}
};
}
AND
public interface PriorityCallable<T> extends Callable<T> {
int getPriority();
}
AND this helper method:
public static ThreadPoolExecutor getPriorityExecutor(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(10, PriorityFuture.COMP)) {
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
RunnableFuture<T> newTaskFor = super.newTaskFor(callable);
return new PriorityFuture<T>(newTaskFor, ((PriorityCallable<T>) callable).getPriority());
}
};
}
AND then use it like this:
class LenthyJob implements PriorityCallable<Long> {
private int priority;
public LenthyJob(int priority) {
this.priority = priority;
}
public Long call() throws Exception {
System.out.println("Executing: " + priority);
long num = 1000000;
for (int i = 0; i < 1000000; i++) {
num *= Math.random() * 1000;
num /= Math.random() * 1000;
if (num == 0)
num = 1000000;
}
return num;
}
public int getPriority() {
return priority;
}
}
public class TestPQ {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ThreadPoolExecutor exec = getPriorityExecutor(2);
for (int i = 0; i < 20; i++) {
int priority = (int) (Math.random() * 100);
System.out.println("Scheduling: " + priority);
LenthyJob job = new LenthyJob(priority);
exec.submit(job);
}
}
}

I will try to explain this problem with a fully functional code. But before diving into the code I would like to explain about PriorityBlockingQueue
PriorityBlockingQueue : PriorityBlockingQueue is an implementation of BlockingQueue. It accepts the tasks along with their priority and submits the task with the highest priority for execution first. If any two tasks have same priority, then we need to provide some custom logic to decide which task goes first.
Now lets get into the code straightaway.
Driver class : This class creates an executor which accepts tasks and later submits them for execution. Here we create two tasks one with LOW priority and the other with HIGH priority. Here we tell the executor to run a MAX of 1 threads and use the PriorityBlockingQueue.
public static void main(String[] args) {
/*
Minimum number of threads that must be running : 0
Maximium number of threads that can be created : 1
If a thread is idle, then the minimum time to keep it alive : 1000
Which queue to use : PriorityBlockingQueue
*/
PriorityBlockingQueue queue = new PriorityBlockingQueue();
ThreadPoolExecutor executor = new ThreadPoolExecutor(0,1,
1000, TimeUnit.MILLISECONDS,queue);
MyTask task = new MyTask(Priority.LOW,"Low");
executor.execute(new MyFutureTask(task));
task = new MyTask(Priority.HIGH,"High");
executor.execute(new MyFutureTask(task));
}
MyTask class : MyTask implements Runnable and accepts priority as an argument in the constructor. When this task runs, it prints a message and then puts the thread to sleep for 1 second.
public class MyTask implements Runnable {
public int getPriority() {
return priority.getValue();
}
private Priority priority;
public String getName() {
return name;
}
private String name;
public MyTask(Priority priority,String name){
this.priority = priority;
this.name = name;
}
#Override
public void run() {
System.out.println("The following Runnable is getting executed "+getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
MyFutureTask class : Since we are using PriorityBlocingQueue for holding our tasks, our tasks must be wrapped inside FutureTask and our implementation of FutureTask must implement Comparable interface. The Comparable interface compares the priority of 2 different tasks and submits the task with the highest priority for execution.
public class MyFutureTask extends FutureTask<MyFutureTask>
implements Comparable<MyFutureTask> {
private MyTask task = null;
public MyFutureTask(MyTask task){
super(task,null);
this.task = task;
}
#Override
public int compareTo(MyFutureTask another) {
return task.getPriority() - another.task.getPriority();
}
}
Priority class : Self explanatory Priority class.
public enum Priority {
HIGHEST(0),
HIGH(1),
MEDIUM(2),
LOW(3),
LOWEST(4);
int value;
Priority(int val) {
this.value = val;
}
public int getValue(){
return value;
}
}
Now when we run this example, we get the following output
The following Runnable is getting executed High
The following Runnable is getting executed Low
Even though we submitted the LOW priority first, but HIGH priority task later, but since we are using a PriorityBlockingQueue, an task with a higher priority will execute first.

My solution:
public class XThreadPoolExecutor extends ThreadPoolExecutor
{
public XThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, PriorityBlockingQueue<Runnable> workQueue)
{
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
public XThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, PriorityBlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler)
{
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
}
public XThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, PriorityBlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
{
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
}
public XThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, PriorityBlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory, RejectedExecutionHandler handler)
{
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value)
{
return new ComparableFutureTask<>(runnable, value);
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable)
{
return new ComparableFutureTask<>(callable);
}
protected class ComparableFutureTask<V>
extends FutureTask<V> implements Comparable<ComparableFutureTask<V>>
{
private Object object;
public ComparableFutureTask(Callable<V> callable)
{
super(callable);
object = callable;
}
public ComparableFutureTask(Runnable runnable, V result)
{
super(runnable, result);
object = runnable;
}
#Override
#SuppressWarnings("unchecked")
public int compareTo(ComparableFutureTask<V> o)
{
if (this == o)
{
return 0;
}
if (o == null)
{
return -1; // high priority
}
if (object != null && o.object != null)
{
if (object.getClass().equals(o.object.getClass()))
{
if (object instanceof Comparable)
{
return ((Comparable) object).compareTo(o.object);
}
}
}
return 0;
}
}
}

It looks like they left that out of apache harmony. There is a svn commit log about a year ago fixing the absence of newTaskFor. You can probably just override the submit functions in an extended ThreadPoolExecutor to create an extended FutureTask that is Comparable. They are not very long.

To answer your question: The newTaskFor() method is found in ThreadPoolExecutor's superclass, AbstractExecutorService. You can simply override it in ThreadPoolExecutor, however.

This answer is a simplified version of #StanislavVitvitskyy's answer. Thanks to him.
I wanted to make the jobs that I submitted be Comparable. I created an ExecutorService with a PriorityBlockingQueue and extend it to handle the newTaskFor(...) methods:
ExecutorService pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepAliveTime, timeUnit, new PriorityBlockingQueue<Runnable>()) {
#Override
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new ComparableFutureTask<T>(runnable, value);
}
#Override
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new ComparableFutureTask<T>(callable);
};
};
I defined a ComparableFutureTask which extends FutureTask and implements Comparable by delegating to the job.compareTo(...) that are submitted to the pool.
public class ComparableFutureTask<T> extends FutureTask<T>
implements Comparable<Object> {
private final Comparable<Object> comparableJob;
#SuppressWarnings("unchecked")
public ComparableFutureTask(Runnable runnable, T value) {
super(runnable, value);
this.comparableJob = (Comparable<Object>) runnable;
}
#SuppressWarnings("unchecked")
public ComparableFutureTask(Callable<T> callable) {
super(callable);
this.comparableJob = (Comparable<Object>) callable;
}
#Override
public int compareTo(Object o) {
return this.comparableJob
.compareTo(((ComparableFutureTask<?>) o).comparable);
}
}
This ExecutorService then can handle Runnable or Callable jobs that are also Comparable. For example:
public class MyJob implements Runnable, Comparable<MyJob> {
private int priority;
...
#Override
public int compareTo(MyJob other) {
// we want higher priority to go first
return other.priority - this.priority;
}
...
}
It is important to note that if you submit a job that is not Comparable to this queue, it will throw a ClassCastException.

Related

Is it possible to make a ThreadPoolExecutor with a PriorityBlockingQueue<Callable<Object>> queue?

I have tried to write a code for an assignment that I have. In that assignment I need to create a callable and to put it in a threadpool with a priority. I didnt succeed because I am unable to put a callable<Object> queue in the ThreadPoolExecutor queue section.
Here is the customExecuter code which uses ThreadPoolExecuter. the queue part in the new ThreadPoolExecuter is marked in red.
package Ex2Part2;
import java.util.concurrent.*;
public class CustomExecutor {
private final int minPoolSize;
private final int maxPoolSize;
private final PriorityBlockingQueue<Callable<Object>> queue;
private final ThreadPoolExecutor executor;
private int maxPriority = 0;
private boolean isShutdown = false;
public CustomExecutor() {
int availableProcessors = Runtime.getRuntime().availableProcessors();
this.minPoolSize = availableProcessors / 2;
this.maxPoolSize = availableProcessors - 1;
this.queue = new PriorityBlockingQueue<>();
this.executor = new ThreadPoolExecutor(minPoolSize, maxPoolSize, 0, TimeUnit.MILLISECONDS, queue);
}
public <T> CompletableFuture<T> submit(Callable<T> task, TaskType priority) throws Exception {
if (isShutdown) {
throw new IllegalStateException("Thread pool has already been shut down");
}
CompletableFuture<T> future = new CompletableFuture<>();
Task callable = new Task(task, priority);
future=callable.call();
maxPriority = Math.max(maxPriority, priority.getPriorityValue());
queue.put(callable);
return future;
}
public int getCurrentMax() {
return maxPriority;
}
public void gracefullyTerminate() {
isShutdown = true;
executor.shutdown();
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
// Handle interrupted exception
}
}
}
Here is my task code:
public class Task implements Callable<Object> {
private Callable<?> task;
private TaskType priority;
public Task(Callable<?> task, TaskType priority) {
this.task = task;
this.priority = priority;
}
public static <T> Task createTask(Callable<T> task,TaskType priority)
{
Task newTask=new Task(task,priority);
return newTask;
}
#Override
public Object call() throws Exception {
return task.call();
}
public int Comperator(Task other) {
return Integer.compare(priority.getPriorityValue(), other.priority.getPriorityValue());
}
}
Another question that I have is in the CustomExecuter in the submit part I dont know how to return the future part so that I will be able to get it in my tests.
......................

Retriable threadpool by overriding afterExecute(Runnable r, Throwable t)

I want to implement a threadpool that tasks can be executed for certain times by overriding afterExecute hook. Can I just submit the argument Runnable r again?
Here is my initial implementation.
public class RetriableThreadPool extends ThreadPoolExecutor {
static final int MAXRETRYTIMES = 5;
int retryTimes = 0;
public RetriableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
retryTimes = 0;
}
#Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (retryTimes < MAXRETRYTIMES) {
retryTimes++;
super.submit(r);
}
}
}
In this initial implementation, I just allow one task submitted.
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class ThreadPoolTest {
public static void main(String[] args) {
RetriableThreadPool retriableThreadPool = new RetriableThreadPool(10, 10, 0L,
TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
retriableThreadPool.execute(new Runnable() {
int num = 0;
#Override
public void run() {
// TODO Auto-generated method stub
num = num + 123;
System.out.println(num);
}
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// retriableThreadPool.shutdown();
}
}
In this example, I got weird output:
123
246
If the runnable can be resubmitted, I think I should get 5 outputs. If this cannot be resubmitted. Only 123 should be the result. I don't understand the reason of this output.
I modified the code thanks to nogard
public class RetriableThreadPool extends ThreadPoolExecutor {
static final int MAXRETRYTIMES = 5;
int retryTimes = 0;
public RetriableThreadPool(int corePoolSize, int maximumPoolSize, long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
retryTimes = 0;
}
#Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
if (retryTimes < MAXRETRYTIMES) {
retryTimes++;
super.execute(r);
}
}
}
I have 3 other questions:
How to retry the runnable with the original state. In this case, I expected the results would be 5 times of 123
How to add hooks for method submit just like afterExecute for execute
Is there a good implementation of retriable threadpool already? I want to runnable is retried when exceptions are thrown or callable returns certain results.
I think the reason of such behavior is that you submit task in the afterExecute method instead of execute, and submit will not trigger afterExecute callback again. That's why you see only 2 lines in the output: first one is from original execute, and the second one is from submit.
Moreover, you never increment retry counter, your task will be always resubmitted
#Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
++ retryTimes;
if (retryTimes < MAXRETRYTIMES) {
super.execute(r);
}
}
Update for your 3 questions:
There are multiple options:
don't change the state inside Runnable (don't assign to num)
create new instance of Runnable (or copy instance)
reset the state of Runnable
For the hook, I would implement in with Decorator pattern: something like this:
public class YourExecutor {
#Override
public void submit(Runnable task) {
return super.submit(new TaskDecorator(task));
}
protected void onCompletedTask(Runnable task) {
// callback
}
private class TaskDecorator implements Runnable {
private final Runnable delegate;
public TaskDecorator(Runnable delegate) {
this.delegate = delegate;
}
#Override
public void run() {
this.delegate.run();
onCompletedTask(delegate);
}
}

How to stop all tasks in the pool except last one?

I have a fixed pool with a single thread. When I submit new task I want to stop all old threads except last one.
private class MyPool extends ThreadPoolExecutor {
public MyPool(long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
super(1, 1, keepAliveTime, unit, workQueue);
}
public boolean isReady() {
return semaphore;
}
#Override
public <T> Future<T> submit(Callable<T> task) {
// Iterate all existed task and stop
Future<T> future = super.submit(task);
return future;
}
private volatile boolean semaphore;
}
Code of running task:
private class MyTask implements Runnable {
private volatile boolean isRun = true;
private int id;
public MyTask(int id) {
this.id = id;
}
public void stop() {
isRun = false;
}
#Override
public void run() {
try {
System.out.println("Start " + id);
if (isRun) {
Thread.sleep(1000);
System.out.println("Stop " + id);
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
I created my own class, but It doesn't correctly work because semaphore effects on a new task as well. What is the best way to do it?
This ThreadPoolExecutor kills the running thread if a new Callable is submitted:
class MyPool extends ThreadPoolExecutor {
private volatile Thread activeThread = null;
private static final Field FutureTask$runner;
static {
try {
FutureTask$runner = FutureTask.class.getDeclaredField("runner");
FutureTask$runner.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new Error(e);
}
}
private static Thread getThread(FutureTask<?> task) {
try {
return (Thread) FutureTask$runner.get(task);
} catch (IllegalAccessException e) {
throw new Error(e);
}
}
public MyPool() {
super(1, 1,
//whatever here
5000, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>());
}
#Override
public <T> Future<T> submit(Callable<T> task) {
if(activeThread != null) {
activeThread.stop(); //kill
}
FutureTask<T> activeTask = (FutureTask<T>)super.submit(task);
activeThread = getThread(activeTask); //steal thread reference for killing
return activeTask;
}
}

Determine when a ScheduledExecutorService will fire next

Is there a way to determine the current millisecond or other time measure of when a ScheduledExecutorService is going to fire next?
scheduleTaskExecutorUpdate = Executors.newSingleThreadScheduledExecutor();
I have a longer running ScheduledExecutorService(A) and from a shorter running ScheduledExecutorService(B) I would like to update a TextView, display a countdown of when ScheduledExecutorService(A) is going to fire next.
If you keep track of the ScheduledFutures for all tasks scheduled with the executor, then yes. This becomes a problem of determining the minimum delay until the next task must fire, which should be a fairly reliable estimate.
final Collection<ScheduledFuture<?>> futures = ...;
/* for each schedule, add it to the above collection */
...
final long delay = Collections.min(futures).getDelay(TimeUnit.MILLISECONDS);
... or, for one task, you merely do:
final ScheduledFuture<?> future = ...;
final long delay = future.getDelay(TimeUnit.MILLISECONDS);
Now, if you're going to be doing it a lot, with mutiple tasks, I'd suggest you maintain a DelayQueue. However, you can't merely throw the ScheduledFutures in the queue without maintaining the changes caused by periodic tasks. Luckily, the class ScheduledThreadPoolExecutor should handle this nicely via its decorateTask methods.
Note this means you will need to create your ownScheduledThreadPoolExecutor directly. Something like the below might work.
public class TrackingSingleThreadScheduledExecutor
extends ScheduledThreadPoolExecutor {
private final DelayQueue<ScheduledFuture<?>> tasks
= new DelayQueue<RunnableScheduledFuture<?>>();
public TrackingSingleThreadScheduledExecutor() {
super(1);
}
public DelayQueue<? extends ScheduledFuture<V>> tasks() {
return tasks;
}
public ScheduledFuture<V> next() {
return tasks.peek();
}
protected <V> RunnableScheduledFuture<V> decorateTask
(final Callable<V> callable, final RunnableScheduledFuture<V> task) {
return new QueueAwareTask(task);
}
protected <V> RunnableScheduledFuture<V> decorateTask
(final Runnable runnable, final RunnableScheduledFuture<V> task) {
return new QueueAwareTask(task);
}
private final class QueueAwareTask<V> implements RunnableScheduledFuture<V> {
private final RunnableScheduledFuture<V> inner;
public QueueAwareTask(final RunnableScheduledFuture<V> inner) {
this.inner = inner;
}
public boolean isPeriodic() {
return inner.isPeriodic();
}
public long getDelay(final TimeUnit unit) {
return inner.getDelay(unit);
}
public void run() {
inner.run();
if (queue.remove(inner) && inner.isPeriodic()
&& !inner.isCancelled()) {
queue.add(inner);
}
}
public int compareTo(final Delayed other) {
return inner.compareTo(other);
}
public boolean cancel(final boolean mayInterruptIfRunning) {
final boolean cancelled = inner.cancel(mayInterruptIfRunning);
if (cancelled) {
queue.remove(inner);
}
return cancelled;
}
public boolean isCancelled() {
return inner.isCancelled();
}
public boolean isDone() {
return inner.isDone();
}
public V get() throws InterruptedException, ExecutionException {
return inner.get();
}
public V get(final long timeout, final TimeUnit unit)
throws InterruptedException, ExecutionException {
return inner.get(timeout, unit);
}
}
}
Then, usage is as follows.
final TrackingSingleThreadScheduledExecutor executor
= new TrackingSingleThreadScheduledExecutor();
...
final long delay = executor.next().getDelay(TimeUnit.MILLISECONDS);

How do I implement task prioritization using an ExecutorService in Java 5?

I am implementing a thread pooling mechanism in which I'd like to execute tasks of varying priorities. I'd like to have a nice mechanism whereby I can submit a high priority task to the service and have it be scheduled before other tasks. The priority of the task is an intrinsic property of the task itself (whether I express that task as a Callable or a Runnable is not important to me).
Now, superficially it looks like I could use a PriorityBlockingQueue as the task queue in my ThreadPoolExecutor, but that queue contains Runnable objects, which may or may not be the Runnable tasks I've submitted to it. Moreover, if I've submitted Callable tasks, it's not clear how this would ever map.
Is there a way to do this? I'd really rather not roll my own for this, since I'm far more likely to get it wrong that way.
(An aside; yes, I'm aware of the possibility of starvation for lower-priority jobs in something like this. Extra points (?!) for solutions that have a reasonable guarantee of fairness)
I have solved this problem in a reasonable fashion, and I'll describe it below for future reference to myself and anyone else who runs into this problem with the Java Concurrent libraries.
Using a PriorityBlockingQueue as the means for holding onto tasks for later execution is indeed a movement in the correct direction. The problem is that the PriorityBlockingQueue must be generically instantiated to contain Runnable instances, and it is impossible to call compareTo (or similiar) on a Runnable interface.
Onto solving the problem. When creating the Executor, it must be given a PriorityBlockingQueue. The queue should further be given a custom Comparator to do proper in place sorting:
new PriorityBlockingQueue<Runnable>(size, new CustomTaskComparator());
Now, a peek at CustomTaskComparator:
public class CustomTaskComparator implements Comparator<MyType> {
#Override
public int compare(MyType first, MyType second) {
return comparison;
}
}
Everything looking pretty straight forward up to this point. It gets a bit sticky here. Our next problem is to deal with the creation of FutureTasks from the Executor. In the Executor, we must override newTaskFor as so:
#Override
protected <V> RunnableFuture<V> newTaskFor(Callable<V> c) {
//Override the default FutureTask creation and retrofit it with
//a custom task. This is done so that prioritization can be accomplished.
return new CustomFutureTask(c);
}
Where c is the Callable task that we're trying to execute. Now, let's have a peek at CustomFutureTask:
public class CustomFutureTask extends FutureTask {
private CustomTask task;
public CustomFutureTask(Callable callable) {
super(callable);
this.task = (CustomTask) callable;
}
public CustomTask getTask() {
return task;
}
}
Notice the getTask method. We're gonna use that later to grab the original task out of this CustomFutureTask that we've created.
And finally, let's modify the original task that we were trying to execute:
public class CustomTask implements Callable<MyType>, Comparable<CustomTask> {
private final MyType myType;
public CustomTask(MyType myType) {
this.myType = myType;
}
#Override
public MyType call() {
//Do some things, return something for FutureTask implementation of `call`.
return myType;
}
#Override
public int compareTo(MyType task2) {
return new CustomTaskComparator().compare(this.myType, task2.myType);
}
}
You can see that we implement Comparable in the task to delegate to the actual Comparator for MyType.
And there you have it, customized prioritization for an Executor using the Java libraries! It takes some bit of bending, but it's the cleanest that I've been able to come up with. I hope this is helpful to someone!
At first blush it would seem you could define an interface for your tasks that extends Runnable or Callable<T> and Comparable. Then wrap a ThreadPoolExecutor with a PriorityBlockingQueue as the queue, and only accept tasks that implement your interface.
Taking your comment into account, it looks like one option is to extend ThreadPoolExecutor, and override the submit() methods. Refer to AbstractExecutorService to see what the default ones look like; all they do is wrap the Runnable or Callable in a FutureTask and execute() it. I'd probably do this by writing a wrapper class that implements ExecutorService and delegates to an anonymous inner ThreadPoolExecutor. Wrap them in something that has your priority, so that your Comparator can get at it.
You can use these helper classes:
public class PriorityFuture<T> implements RunnableFuture<T> {
private RunnableFuture<T> src;
private int priority;
public PriorityFuture(RunnableFuture<T> other, int priority) {
this.src = other;
this.priority = priority;
}
public int getPriority() {
return priority;
}
public boolean cancel(boolean mayInterruptIfRunning) {
return src.cancel(mayInterruptIfRunning);
}
public boolean isCancelled() {
return src.isCancelled();
}
public boolean isDone() {
return src.isDone();
}
public T get() throws InterruptedException, ExecutionException {
return src.get();
}
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return src.get(timeout, unit);
}
public void run() {
src.run();
}
public static Comparator<Runnable> COMP = new Comparator<Runnable>() {
public int compare(Runnable o1, Runnable o2) {
if (o1 == null && o2 == null)
return 0;
else if (o1 == null)
return -1;
else if (o2 == null)
return 1;
else {
int p1 = ((PriorityFuture<?>) o1).getPriority();
int p2 = ((PriorityFuture<?>) o2).getPriority();
return p1 > p2 ? 1 : (p1 == p2 ? 0 : -1);
}
}
};
}
AND
public interface PriorityCallable<T> extends Callable<T> {
int getPriority();
}
AND this helper method:
public static ThreadPoolExecutor getPriorityExecutor(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS,
new PriorityBlockingQueue<Runnable>(10, PriorityFuture.COMP)) {
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
RunnableFuture<T> newTaskFor = super.newTaskFor(callable);
return new PriorityFuture<T>(newTaskFor, ((PriorityCallable<T>) callable).getPriority());
}
};
}
AND then use it like this:
class LenthyJob implements PriorityCallable<Long> {
private int priority;
public LenthyJob(int priority) {
this.priority = priority;
}
public Long call() throws Exception {
System.out.println("Executing: " + priority);
long num = 1000000;
for (int i = 0; i < 1000000; i++) {
num *= Math.random() * 1000;
num /= Math.random() * 1000;
if (num == 0)
num = 1000000;
}
return num;
}
public int getPriority() {
return priority;
}
}
public class TestPQ {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ThreadPoolExecutor exec = getPriorityExecutor(2);
for (int i = 0; i < 20; i++) {
int priority = (int) (Math.random() * 100);
System.out.println("Scheduling: " + priority);
LenthyJob job = new LenthyJob(priority);
exec.submit(job);
}
}
}
I will try to explain this problem with a fully functional code. But before diving into the code I would like to explain about PriorityBlockingQueue
PriorityBlockingQueue : PriorityBlockingQueue is an implementation of BlockingQueue. It accepts the tasks along with their priority and submits the task with the highest priority for execution first. If any two tasks have same priority, then we need to provide some custom logic to decide which task goes first.
Now lets get into the code straightaway.
Driver class : This class creates an executor which accepts tasks and later submits them for execution. Here we create two tasks one with LOW priority and the other with HIGH priority. Here we tell the executor to run a MAX of 1 threads and use the PriorityBlockingQueue.
public static void main(String[] args) {
/*
Minimum number of threads that must be running : 0
Maximium number of threads that can be created : 1
If a thread is idle, then the minimum time to keep it alive : 1000
Which queue to use : PriorityBlockingQueue
*/
PriorityBlockingQueue queue = new PriorityBlockingQueue();
ThreadPoolExecutor executor = new ThreadPoolExecutor(0,1,
1000, TimeUnit.MILLISECONDS,queue);
MyTask task = new MyTask(Priority.LOW,"Low");
executor.execute(new MyFutureTask(task));
task = new MyTask(Priority.HIGH,"High");
executor.execute(new MyFutureTask(task));
task = new MyTask(Priority.MEDIUM,"Medium");
executor.execute(new MyFutureTask(task));
}
MyTask class : MyTask implements Runnable and accepts priority as an argument in the constructor. When this task runs, it prints a message and then puts the thread to sleep for 1 second.
public class MyTask implements Runnable {
public int getPriority() {
return priority.getValue();
}
private Priority priority;
public String getName() {
return name;
}
private String name;
public MyTask(Priority priority,String name){
this.priority = priority;
this.name = name;
}
#Override
public void run() {
System.out.println("The following Runnable is getting executed "+getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
MyFutureTask class : Since we are using PriorityBlocingQueue for holding our tasks, our tasks must be wrapped inside FutureTask and our implementation of FutureTask must implement Comparable interface. The Comparable interface compares the priority of 2 different tasks and submits the task with the highest priority for execution.
public class MyFutureTask extends FutureTask<MyFutureTask>
implements Comparable<MyFutureTask> {
private MyTask task = null;
public MyFutureTask(MyTask task){
super(task,null);
this.task = task;
}
#Override
public int compareTo(MyFutureTask another) {
return task.getPriority() - another.task.getPriority();
}
}
Priority class : Self explanatory Priority class.
public enum Priority {
HIGHEST(0),
HIGH(1),
MEDIUM(2),
LOW(3),
LOWEST(4);
int value;
Priority(int val) {
this.value = val;
}
public int getValue(){
return value;
}
}
Now when we run this example, we get the following output
The following Runnable is getting executed High
The following Runnable is getting executed Medium
The following Runnable is getting executed Low
Even though we submitted the LOW priority first, but HIGH priority task later, but since we are using a PriorityBlockingQueue, any task with a higher priority will execute first.
My solution preserves submition order of tasks for same priorities. It's an improvement of this answer
Task execution order is based on:
Priority
Submit order (within same priority)
Tester class:
public class Main {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = PriorityExecutors.newFixedThreadPool(1);
//Priority=0
executorService.submit(newCallable("A1", 200)); //Defaults to priority=0
executorService.execute(newRunnable("A2", 200)); //Defaults to priority=0
executorService.submit(PriorityCallable.of(newCallable("A3", 200), 0));
executorService.submit(PriorityRunnable.of(newRunnable("A4", 200), 0));
executorService.execute(PriorityRunnable.of(newRunnable("A5", 200), 0));
executorService.submit(PriorityRunnable.of(newRunnable("A6", 200), 0));
executorService.execute(PriorityRunnable.of(newRunnable("A7", 200), 0));
executorService.execute(PriorityRunnable.of(newRunnable("A8", 200), 0));
//Priority=1
executorService.submit(PriorityRunnable.of(newRunnable("B1", 200), 1));
executorService.submit(PriorityRunnable.of(newRunnable("B2", 200), 1));
executorService.submit(PriorityCallable.of(newCallable("B3", 200), 1));
executorService.execute(PriorityRunnable.of(newRunnable("B4", 200), 1));
executorService.submit(PriorityRunnable.of(newRunnable("B5", 200), 1));
executorService.shutdown();
}
private static Runnable newRunnable(String name, int delay) {
return new Runnable() {
#Override
public void run() {
System.out.println(name);
sleep(delay);
}
};
}
private static Callable<Integer> newCallable(String name, int delay) {
return new Callable<Integer>() {
#Override
public Integer call() throws Exception {
System.out.println(name);
sleep(delay);
return 10;
}
};
}
private static void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
}
Result:
A1 B1 B2 B3 B4 B5 A2 A3 A4 A5 A6 A7 A8
First task is A1 because there were no higher priority in the queue when it was inserted. B tasks are 1 priority so executed earlier, A tasks are 0 priority so executed later, but execution order is follows submition order: B1, B2, B3, ... A2, A3, A4 ...
The solution:
public class PriorityExecutors {
public static ExecutorService newFixedThreadPool(int nThreads) {
return new PriorityExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS);
}
private static class PriorityExecutor extends ThreadPoolExecutor {
private static final int DEFAULT_PRIORITY = 0;
private static AtomicLong instanceCounter = new AtomicLong();
#SuppressWarnings({"unchecked"})
public PriorityExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, (BlockingQueue) new PriorityBlockingQueue<ComparableTask>(10,
ComparableTask.comparatorByPriorityAndSequentialOrder()));
}
#Override
public void execute(Runnable command) {
// If this is ugly then delegator pattern needed
if (command instanceof ComparableTask) //Already wrapped
super.execute(command);
else {
super.execute(newComparableRunnableFor(command));
}
}
private Runnable newComparableRunnableFor(Runnable runnable) {
return new ComparableRunnable(ensurePriorityRunnable(runnable));
}
#Override
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new ComparableFutureTask<>(ensurePriorityCallable(callable));
}
#Override
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new ComparableFutureTask<>(ensurePriorityRunnable(runnable), value);
}
private <T> PriorityCallable<T> ensurePriorityCallable(Callable<T> callable) {
return (callable instanceof PriorityCallable) ? (PriorityCallable<T>) callable
: PriorityCallable.of(callable, DEFAULT_PRIORITY);
}
private PriorityRunnable ensurePriorityRunnable(Runnable runnable) {
return (runnable instanceof PriorityRunnable) ? (PriorityRunnable) runnable
: PriorityRunnable.of(runnable, DEFAULT_PRIORITY);
}
private class ComparableFutureTask<T> extends FutureTask<T> implements ComparableTask {
private Long sequentialOrder = instanceCounter.getAndIncrement();
private HasPriority hasPriority;
public ComparableFutureTask(PriorityCallable<T> priorityCallable) {
super(priorityCallable);
this.hasPriority = priorityCallable;
}
public ComparableFutureTask(PriorityRunnable priorityRunnable, T result) {
super(priorityRunnable, result);
this.hasPriority = priorityRunnable;
}
#Override
public long getInstanceCount() {
return sequentialOrder;
}
#Override
public int getPriority() {
return hasPriority.getPriority();
}
}
private static class ComparableRunnable implements Runnable, ComparableTask {
private Long instanceCount = instanceCounter.getAndIncrement();
private HasPriority hasPriority;
private Runnable runnable;
public ComparableRunnable(PriorityRunnable priorityRunnable) {
this.runnable = priorityRunnable;
this.hasPriority = priorityRunnable;
}
#Override
public void run() {
runnable.run();
}
#Override
public int getPriority() {
return hasPriority.getPriority();
}
#Override
public long getInstanceCount() {
return instanceCount;
}
}
private interface ComparableTask extends Runnable {
int getPriority();
long getInstanceCount();
public static Comparator<ComparableTask> comparatorByPriorityAndSequentialOrder() {
return (o1, o2) -> {
int priorityResult = o2.getPriority() - o1.getPriority();
return priorityResult != 0 ? priorityResult
: (int) (o1.getInstanceCount() - o2.getInstanceCount());
};
}
}
}
private static interface HasPriority {
int getPriority();
}
public interface PriorityCallable<V> extends Callable<V>, HasPriority {
public static <V> PriorityCallable<V> of(Callable<V> callable, int priority) {
return new PriorityCallable<V>() {
#Override
public V call() throws Exception {
return callable.call();
}
#Override
public int getPriority() {
return priority;
}
};
}
}
public interface PriorityRunnable extends Runnable, HasPriority {
public static PriorityRunnable of(Runnable runnable, int priority) {
return new PriorityRunnable() {
#Override
public void run() {
runnable.run();
}
#Override
public int getPriority() {
return priority;
}
};
}
}
}
Would it be possible to have one ThreadPoolExecutor for each level of priority? A ThreadPoolExecutor can be instanciated with a ThreadFactory and you could have your own implementation of a ThreadFactory to set the different priority levels.
class MaxPriorityThreadFactory implements ThreadFactory {
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
thread.setPriority(Thread.MAX_PRIORITY);
}
}

Categories

Resources