问题
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
回答1:
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
回答2:
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);
}
}
}
回答3:
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;
}
}
}
回答4:
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.
回答5:
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.
回答6:
To answer your question: The newTaskFor()
method is found in ThreadPoolExecutor
's superclass, AbstractExecutorService
. You can simply override it in ThreadPoolExecutor
, however.
回答7:
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
.
来源:https://stackoverflow.com/questions/3545623/how-to-implement-priorityblockingqueue-with-threadpoolexecutor-and-custom-tasks