Java 异构任务集的动态优先级

Java 异构任务集的动态优先级,java,scheduler,job-scheduling,Java,Scheduler,Job Scheduling,我有一大堆重复的任务要安排。他们查询数据库,找出该做什么,然后执行一些操作,如更新统计数据、发送电子邮件、获取文件和导入它们。目前,大概有十家,这个数字预计会增长很多。我没有任何时间限制,事实上,我的工作就是选择一种算法,这样就不会有人抱怨了D 目前,我正在使用线程和定期计划任务的临时组合,如 对于最重要的任务,有一个自己的线程在空闲时返回到短睡眠状态(当新的重要工作到来时,它可以从中被唤醒) 另一个重要任务在其自己的线程中每小时调度一次 定期安排中等重要性的任务来“填补漏洞”,因此在任何时候

我有一大堆重复的任务要安排。他们查询数据库,找出该做什么,然后执行一些操作,如更新统计数据、发送电子邮件、获取文件和导入它们。目前,大概有十家,这个数字预计会增长很多。我没有任何时间限制,事实上,我的工作就是选择一种算法,这样就不会有人抱怨了D

目前,我正在使用线程和定期计划任务的临时组合,如

  • 对于最重要的任务,有一个自己的线程在空闲时返回到短睡眠状态(当新的重要工作到来时,它可以从中被唤醒)
  • 另一个重要任务在其自己的线程中每小时调度一次
  • 定期安排中等重要性的任务来“填补漏洞”,因此在任何时候可能只有一个任务在运行
  • 最不重要的任务都由一个专用线程处理
目前它似乎运行良好,但它不是未来的证明,并且由于以下原因,它感觉不正确:

  • 由于最不重要任务的队列可能会大量增加,因此此类任务可能会无限期延迟
  • 填补漏洞可能会出错,并且可能会同时运行许多任务
  • 在任何给定时刻运行的任务数应取决于服务器负载。(*)
(*)它主要是一个web服务器,服务请求实际上是最高优先级。获得一个单独的服务器不会有帮助,因为瓶颈通常是数据库。目前,它运行良好,但我正在寻找更好的解决方案,因为我们希望负载在一两年内增长100倍

我的想法是增加工作的优先级,当它被延迟太多的时候。例如,有些统计数据每小时运行一次,延迟几个小时没有什么大不了的,但它不应该是一整天,也不应该是一周

我很乐意将所有的
AbstractExecutionThreadService
s和
AbstractScheduledService
s替换为如下工作方式:

  • 无论发生什么情况,立即启动最高优先级的任务
  • 仅当总负载为“小”时才启动中等优先级任务
  • 仅在系统“大部分空闲”时启动最低优先级任务
  • 使用提供的公式增加延迟任务的优先级
这听起来确实很模糊,让它更精确是我要求的一部分。我的竞争目标是

  • 不要无谓地拖延重要任务
  • 不要让太多并发运行的任务过多地降低服务器的速度

没有硬性的截止日期,也没有必要最小化使用的线程数量。我并不坚持要一个解决方案完全按照我所描述的那样去做,我不是在寻找一个库(我也不坚持要重新发明轮子)。我认为类似cron的调度器不是正确的解决方案。

我认为您的计划与您想要的非常接近,可能只需要一点鼓励/批准/鼓励

我的想法是“如果我知道我可以运行的并发线程的最大数量,那么我将如何在3个线程队列中共享这些线程”

一旦我知道了这一点,我就可以设置3个队列,每个队列都有不同的可用线程份额。 -优先级1(最高)获得50%的工作 -优先级2得到35%的工作
-优先级3(最低)获得15%的工作

使用
执行器服务
模型,重新排序执行器任务的经典解决方案是创建
线程池执行器
,并使用
优先级阻塞队列
为其提供任务-如前所述

然而,同样需要安排任务也会让事情变得复杂
ScheduledThreadPoolExecutor
使用内部自定义
BlockingQueue
在计划就绪时输入任务,但我认为您很清楚,它不容易进行进一步的自定义

乍一看,
DelayQueue
看起来非常适合这项任务——它可以为下一个
Delayed
元素或任务确定优先级。这将通过
Delayed.getDelay()
处理关于是否准备就绪的延迟决策


此计划的美中不足之处在于,您试图将类似于
DelayQueue
的内容传递给
ThreadPoolExecutor
的构造函数。这只接受
BlockingQueue
,而不接受
BlockingQueue虽然这个问题确实很有趣,但我怀疑这种格式是否适合StackOverflow。我是从眼睛的角度说这句话的,绝不是屈尊于一个拥有丰富经验的用户,从他的声誉来看,他应该比问一个尽管你很好,但没有人能以非自以为是的方式回答的问题更清楚(但抽象为“无代码”)和有趣的问题描述。说了这句话,并试图表现出善意,你看了吗?在这个网站上,你还可以找到一本关于基本概念和常见问题的食谱。我从来没有使用过它,但它可能会帮助您完成“不要再发明轮子”部分,并可能让您集中精力正确地获取调度参数。@kriegaex当然,我使用过。它有很多有用的特性,但它们似乎与我想要的是正交的。我列出的两个相互竞争的目标似乎没有通过谷歌搜索得到实现<代码>++
事实上,我的大部分问题只是上下文。我想,这就是为什么它听起来很模糊。通过定义一个目标函数,可以很容易地确定列出的目标,然后我们将得到一个优化问题。您刚才描述了为什么这个问题不适合这样。谢谢你用粗体字突出显示。但无论如何,祝你好运,这只是反馈,没有冒犯的意思。我要离开这里了。@kriegaex,恐怕我跟不上。是一个我没有精确描述的问题,还是一个优化问题?关于
public class BlockingDelayQueue extends AbstractQueue<Runnable>
        implements BlockingQueue<Runnable> {

    private final DelayQueue<DelayedRunnable> delayQueue;

    public BlockingDelayQueue(DelayQueue<DelayedRunnable> delayQueue) {
        this.delayQueue = delayQueue;
    }

    @Override
    public boolean isEmpty() {
        return delayQueue.isEmpty();
    }

    @Override
    public Runnable poll(long timeout, TimeUnit unit)
            throws InterruptedException {
        DelayedRunnable delayedRunnable = delayQueue.poll(timeout, unit);

        if (delayedRunnable == null)
            return null;
        return delayedRunnable.getCommand();
    }
    ...
}
LOW {
    boolean isReady(ThreadPoolExecutor executor) {
        return executor.getActiveCount() == 0;
    }
},
MEDIUM {
    boolean isReady(ThreadPoolExecutor executor) {
        return executor.getActiveCount() <= 1;
    }
},
HIGH {
    boolean isReady(ThreadPoolExecutor executor) {
        return true;
    }
};
@Override
public long getDelay(TimeUnit unit) {
    long millis;
    if (!priority.isReady(executor))
        millis = 1000;
    else
        millis = time - System.currentTimeMillis();
    return unit.convert(millis, TimeUnit.MILLISECONDS);
}
    DelayedScheduler scheduler = new DelayedScheduler();
    scheduler.schedule(task("Low 1"), 1, TimeUnit.SECONDS, Priority.LOW);
    scheduler.schedule(task("Low 2"), 2, TimeUnit.SECONDS, Priority.LOW);
    scheduler.schedule(task("Low 3"), 3, TimeUnit.SECONDS, Priority.LOW);
    scheduler.schedule(task("Medium 1"), 1, TimeUnit.SECONDS, Priority.MEDIUM);
    scheduler.schedule(task("Medium 2"), 2, TimeUnit.SECONDS, Priority.MEDIUM);
    scheduler.schedule(task("Medium 3"), 3, TimeUnit.SECONDS, Priority.MEDIUM);
    scheduler.schedule(task("High 1"), 1, TimeUnit.SECONDS, Priority.HIGH);
    scheduler.schedule(task("High 2"), 2, TimeUnit.SECONDS, Priority.HIGH);
    scheduler.schedule(task("High 3"), 3, TimeUnit.SECONDS, Priority.HIGH);
High 1 started at 1087ms
Medium 1 started at 1087ms
High 2 started at 2087ms
  Medium 1 ended at 3087ms
  High 1 ended at 3087ms
High 3 started at 3087ms
  High 2 ended at 4088ms
Medium 2 started at 4088ms
  High 3 ended at 5088ms
Medium 3 started at 5088ms
  Medium 2 ended at 6088ms
  Medium 3 ended at 7089ms
Low 1 started at 7089ms
  Low 1 ended at 9089ms
Low 2 started at 9089ms
  Low 2 ended at 11089ms
Low 3 started at 11089ms
  Low 3 ended at 13089ms