在ScheduledThreadPoolExecutor中使用PriorityBlockingQueue和Comparator

时间:2018-01-13 23:59:02

标签: java multithreading blockingqueue scheduledexecutorservice

首先:我已经阅读了以下两个问题及其可能的解决方案:

我遇到的困境是我想使用自定义BlockingQueue或者更确切地说是一个不同但特定的队列,即PriorityBlockingQueue和自定义Comparator,它按优先级对队列进行排序

ThreadPoolExecutor确实在其构造函数中支持自定义队列,但它不实现ScheduledExecutorService接口中的方法。所以我去找了子类ScheduledThreadPoolExecutor,但它不支持自定义队列,而是使用DelayedWorkQueue

问题:

  • 我无法从ScheduledThreadPoolExecutor扩展,因为为我自己的类创建构造函数不会执行任何操作,因为ScheduledThreadPoolExecutor的构造函数不接受自定义队列作为参数。
  • 我无法复制ThreadPoolExecutor类的内容和ScheduledThreadPoolExecutor的实现,因为它使用了很多用 no modifiers 声明的方法(例如{{ 1}}以及此调用调用的所有方法),这使得我无法访问该方法,因为即使它是canRunInCurrentState(boolean periodic)的子类,它也不在同一个包中。

我目前的实现如下:

ThreadPoolExecutor

正如您所看到的,构造函数问题已得到解决,但它仍然保留了import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import com.croemheld.tasks.PriorityTaskComparator; public class ScheduledPriorityThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService { private static final int INITIAL_QUEUE_SIZE = 10; public ScheduledPriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new PriorityBlockingQueue<Runnable>(INITIAL_QUEUE_SIZE, new PriorityTaskComparator())); } public ScheduledPriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new PriorityBlockingQueue<Runnable>(INITIAL_QUEUE_SIZE, new PriorityTaskComparator()), handler); } public ScheduledPriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new PriorityBlockingQueue<Runnable>(INITIAL_QUEUE_SIZE, new PriorityTaskComparator()), threadFactory); } public ScheduledPriorityThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { super(corePoolSize, maximumPoolSize, keepAliveTime, unit, new PriorityBlockingQueue<Runnable>(INITIAL_QUEUE_SIZE, new PriorityTaskComparator()), threadFactory, handler); } @Override public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) { // TODO Auto-generated method stub return null; } @Override public <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit) { // TODO Auto-generated method stub return null; } @Override public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { // TODO Auto-generated method stub return null; } @Override public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { // TODO Auto-generated method stub return null; } } 的调度方法的实现。

所以我问你,有没有办法可以将ScheduledExecutorService传递给队列或简单而不是太详尽的方式来创建一个自己的执行器类来实现来自Comparator的方法并提供ScheduledExecutorService类的方法以及使用ThreadPoolExecutor

3 个答案:

答案 0 :(得分:2)

如果我理解你的问题,你想定期执行一些任务,但要根据一些自定义优先级。如果没有发明自己的ExecutorService,我建议退一步看看你的设计。您可能希望将计划与优先级和任务执行分开:

  1. 由于ThreadPoolExecutor接受自定义BlockingQueue,您可以轻松实现自己的优先级。然后只需定期从代码中的其他位置提交任务。
  2. 如果您坚持使用ScheduledThreadPoolExecutor,那么您将获得日程安排,但您必须自己实施优先顺序。您可以获得非常有创意,但一个选项可能是让业务流程任务从自定义BlockingQueue中获取任务并提交到池中。

答案 1 :(得分:0)

我寻找其他可能的解决方案,我得出以下结果:

由于ThreadPoolExecutor管理多个Threads的池(即,如果您在Executors.newFixedThreadPool(int nThreads)方法中设置了两个或更多个线程),并且您确实想要混合基于优先级的BlockingQueue,然后我会建议您执行以下操作:

  • 使用带有自定义比较器的ThreadPoolExecutor创建一个类似于上述类的PriorityBlockingQueue类。
  • 创建您自己的Task课程(或FutureTask分机,无论您认为哪种方式最适合您)
  • 这些任务类适用于一次性任务,这意味着它们只运行一次。

对于应该定期在后台运行的循环任务,我想出了一个简单的类来实现这个目的:

public abstract class AbstractThread extends Thread {

    protected Runnable runnable;

    protected AbstractThread(String name, Runnable runnable) {
        super(runnable, name);

        this.runnable = runnable;
    }

    /**
     * This method provides a way to perform some action before the thread is actually starting.
     */
    protected abstract void beforeExecution();

    /**
     * This method provides a way to perform some action after the thread finished.
     */
    protected abstract void afterExecution();

    @Override
    public void run() {
        try {
            doRun();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * Run the given runnable here.
     * 
     * @throws InterruptedException 
     */
    protected abstract void doRun() throws InterruptedException;

}

虽然简单的一次性线程只运行一次runnable:

@Override
protected void doRun() {
    beforeExecution();

    runnable.run();

    afterExecution();
}

线程中的定期任务只会执行以下操作:

@Override
protected void doRun() throws InterruptedException {
    beforeExecution();

    while(!isInterrupted()) {
        runnable.run();
        Thread.sleep(millis);
    }

    afterExecution();
}

如果你想支持一段时间运行一次的周期性任务,你可以将延迟参数传递给Thread实例,或者只是在你的runnable中写一些Thread.sleep(delay)

这不是实际的代码,只是一个建议,因为我现在尝试使用它。

答案 2 :(得分:0)

我使用PriorityBlockingQueue为ThreadPoolExecutor编写了一个简单,干净,有效的解决方案。

public class PriorityQueueThreadPoolExecutor {
    private static final int DEFAULT_INITIAL_PRIORITY_QUEUE_CAPACITY = 100;
    private static final long THREAD_TIMEOUT_IN_SECS = 60L;
    public static final int DEFAULT_PRIORITY = 0;

    private static final AtomicInteger InstanceCounter = new AtomicInteger(0);

    private final ThreadPoolExecutor internalExecutor;

    public PriorityQueueThreadPoolExecutor(int threadPoolSize, String threadNamePrefix) {
        internalExecutor = new ThreadPoolExecutor(threadPoolSize, threadPoolSize, THREAD_TIMEOUT_IN_SECS,
                TimeUnit.SECONDS, createPriorityQueue(), createThreadFactory(threadNamePrefix));
        internalExecutor.allowCoreThreadTimeOut(true);
    }

    public void submit(Runnable runnable, int priority) {
        internalExecutor.execute(new RunnableWithPriority(runnable, priority));
    }

    public void submit(Runnable runnable) {
        submit(runnable, DEFAULT_PRIORITY);
    }

    public ThreadPoolExecutor getInternalThreadPoolExecutor() {
        return internalExecutor;
    }

    private static BlockingQueue<Runnable> createPriorityQueue() {
        return new PriorityBlockingQueue<>(DEFAULT_INITIAL_PRIORITY_QUEUE_CAPACITY,
                new ComparatorForPriorityRunnable());
    }

    private static ThreadFactory createThreadFactory(String threadNamePrefix) {
        return new ThreadFactoryBuilder().setThreadFactory(Executors.defaultThreadFactory())
                .setNameFormat(threadNamePrefix + "-%d").setDaemon(true).build();
    }

    private static class RunnableWithPriority implements Runnable {
        final int creationOrder;
        final int priority;
        final Runnable runnable;

        public RunnableWithPriority(Runnable runnable, int priority) {
            this.runnable = runnable;
            this.priority = priority;
            this.creationOrder = InstanceCounter.incrementAndGet();
        }

        @Override
        public void run() {
            runnable.run();
        }
    }

    private static class ComparatorForPriorityRunnable implements Comparator<Runnable> {
        @Override
        public int compare(Runnable r1, Runnable r2) {
            RunnableWithPriority pr1 = (RunnableWithPriority) r1;
            RunnableWithPriority pr2 = (RunnableWithPriority) r2;
            // higher value means higher priority
            int priorityResult = pr2.priority - pr1.priority;
            return priorityResult != 0 ? priorityResult : (pr1.creationOrder - pr2.creationOrder);
        }
    }
}