我有一堆重复的任务要安排。他们查询数据库以找出要执行的操作,然后执行某些操作,如统计信息更新,发送电子邮件,获取文件和导入文件。目前,可能有十个,这个数字预计会增长很多。我没有给出任何时间限制,实际上,选择算法是我的工作,以便没有人抱怨。 :d
目前,我正在使用线程的临时组合和定期安排的任务,例如
目前似乎运作良好,但它不是面向未来的,并且出于以下原因感觉不对:
(*)它主要是一个Web服务器,服务请求实际上是最高优先级。获得单独的服务器无济于事,因为瓶颈通常是数据库。目前,它运行良好,但我正在寻找一个更好的解决方案,因为我们希望负载在一两年内增长100倍。
我的想法是提高工作的优先级,当它被推迟太多时。例如,有统计数据每小时运行一次并将它们延迟几个小时并不是什么大不了的事,但它不应该是一整天而且不应该是整整一周。
我很乐意通过以下方式替换我的所有AbstractExecutionThreadService
和AbstractScheduledService
:
这肯定听起来很模糊,让它更精确是我要问的一部分。我的竞争目标是
没有硬性截止日期,也没有必要尽量减少使用的线程数。我并不坚持完全按照我所描述的方式解决问题,我不是在寻找一个库(我也没有坚持重新发明轮子)。我不认为类似cron的调度程序是正确的解决方案。
答案 0 :(得分:1)
使用ExecutorService
模型,重新排序执行程序任务的经典解决方案是创建一个ThreadPoolExecutor
,PriorityBlockingQueue
为其提供任务 - 如here所述。< / p>
然而,需要安排任务也会对其产生影响。 ScheduledThreadPoolExecutor
使用内部自定义BlockingQueue
在计划准备就绪时提供任务,但我认为您很清楚,它不容易进行进一步的自定义。
乍一看,DelayQueue
看起来完全符合条款 - 它可以优先考虑下一个Delayed
元素或任务。这会处理Delayed.getDelay()
关于它是否准备就绪的迟到决定。
当你试图将DelayQueue<DelayedRunnable>
之类的东西传递给ThreadPoolExecutor
的构造函数时,美国人用这个计划软膏。这只会接受BlockingQueue<Runnable>
,而不是BlockingQueue<? extends Runnable>
。
解决此问题的一种方法是创建BlockingQueue<Runnable>
的最小实施,该实施委派给BlockingQueue
。基础知识在这里:
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();
}
...
}
DelayedRunnable
的实验版用于证明其中使用简单的Priority
枚举来检查执行者的“繁忙程度”:
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;
}
};
然后可以检查DelayedRunnable.getDelay()
:
@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);
}
- 只要<= 0
尚未就绪,它就不会返回priority
。
这似乎运作良好,例如在这里启动标准的2s睡眠任务......
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
- 允许中等优先级任务,而只有一个高优先级任务正在运行,低,而没有其他任何事情发生。
(DelayedScheduler
和GitHub上的其他未见位。)
答案 1 :(得分:0)
我认为你非常接近你想要的东西,也许只需要一点鼓励/批准/约束
我的想法是&#34;如果我知道我可以运行的最大并发线程数,那么我将如何与3个线程队列分享#34;。
一旦我知道这一点,我就可以设置3个队列,每个队列都有不同的可用线程份额。 - 优先级1(最高)获得50%的工作 - 优先级2获得35%的工作 - 优先级3(最低)获得15%的工作