简体   繁体   English

如何使用ThreadPoolExecutor和自定义任务实现PriorityBlockingQueue

[英]How to implement PriorityBlockingQueue with ThreadPoolExecutor and custom tasks

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. 我有自己的类BaseTask ,它使用ThreadPoolExecutor来处理任务。 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. 我想要任务优先级,但是当我尝试使用PriorityBlockingQueue我得到ClassCastException因为ThreadPoolExecutor将我的Tasks包装到FutureTask对象中。

This obviously makes sense because the FutureTask does not implement Comparable , but how would I go on to solve the priority problem? 这显然是有道理的,因为FutureTask没有实现Comparable ,但我将如何继续解决优先级问题? I've read that you could override newTaskFor() in ThreadPoolExecutor , but I can not seem to find this method at all...? 我已经读过你可以在ThreadPoolExecutor覆盖newTaskFor() ,但我似乎无法找到这个方法......?

Any suggestions would be much appreciated! 我们欢迎所有的建议!

Some code to help: 一些代码可以帮助:

In my BaseTask class I have 在我的BaseTask课程中,我有

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 BaseFutureTask类中

@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 BaseThreadPoolExecutor类中,我重写了3个submit方法...此类中的构造函数被调用,但没有submit方法

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 你可以猜测RunWithPriority是一个抽象类,它是Runnable并且有一个Integer优先级字段

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);
        }
    }
}

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;
        }
    }
}

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 : PriorityBlockingQueue is an implementation of BlockingQueue. PriorityBlockingQueue :PriorityBlockingQueue是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. 在这里,我们创建两个任务,一个具有LOW优先级,另一个具有HIGH优先级。 Here we tell the executor to run a MAX of 1 threads and use the PriorityBlockingQueue. 在这里,我们告诉执行者运行1个线程的MAX并使用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. MyTask类 :MyTask实现Runnable并接受优先级作为构造函数中的参数。 When this task runs, it prints a message and then puts the thread to sleep for 1 second. 当此任务运行时,它会打印一条消息,然后让线程进入休眠状态1秒钟。

   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. MyFutureTask类 :由于我们使用PriorityBlocingQueue来保存我们的任务,我们的任务必须包含在FutureTask中,我们的FutureTask实现必须实现Comparable接口。 The Comparable interface compares the priority of 2 different tasks and submits the task with the highest priority for execution. Comparable接口比较2个不同任务的优先级,并提交具有最高执行优先级的任务。

 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. 尽管我们先提交了LOW优先级,但稍后提交了HIGH优先级任务,但由于我们使用的是PriorityBlockingQueue,因此优先级较高的任务将首先执行。

To answer your question: The newTaskFor() method is found in ThreadPoolExecutor 's superclass, AbstractExecutorService . 回答你的问题: newTaskFor()方法可以在ThreadPoolExecutor的超类AbstractExecutorService You can simply override it in ThreadPoolExecutor , however. 但是,您可以在ThreadPoolExecutor简单地覆盖它。

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 . 大约一年前有一个svn提交日志 ,修复了缺少newTaskFor You can probably just override the submit functions in an extended ThreadPoolExecutor to create an extended FutureTask that is Comparable . 您可以在扩展的ThreadPoolExecutor覆盖submit函数,以创建可Comparable的扩展FutureTask They are not very long . 它们不是很长

This answer is a simplified version of @StanislavVitvitskyy's answer. 这个答案是@StanislavVitvitskyy答案的简化版本。 Thanks to him. 谢谢他。

I wanted to make the jobs that I submitted be Comparable . 我想让我提交的工作成为可Comparable I created an ExecutorService with a PriorityBlockingQueue and extend it to handle the newTaskFor(...) methods: 我创建了一个带有PriorityBlockingQueueExecutorService并扩展它以处理newTaskFor(...)方法:

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. 我定义了一个ComparableFutureTask ,它扩展了FutureTask并通过委托提交给池的job.compareTo(...)来实现Comparable

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 . 然后,此ExecutorService可以处理也是Comparable RunnableCallable作业。 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 . 需要注意的是,如果你提交的作业,不是很重要的Comparable到此队列中,它会抛出一个ClassCastException

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM