Java高效的调度结构?

Java高效的调度结构?,java,algorithm,scheduling,Java,Algorithm,Scheduling,我为这个问题的长度表示歉意,但考虑到我正在寻找解决问题的合适方法,而不是简单的代码建议,我认为包含足够的细节是很重要的 一般说明: 我正在从事一个项目,该项目要求能够以相对重复的时间间隔“安排”任务 这些间隔是以一些内部时间表示的,这些时间表示为一个整数,随着程序的执行而递增(因此不等于实时)。每次发生这种情况时,将对计划进行互选,以检查在此时间步执行的任何任务 如果执行了任务,则应重新安排该任务在相对于当前时间的位置再次运行(例如,在5个时间步内)。此相对位置仅存储为任务对象的整数属性 问题

我为这个问题的长度表示歉意,但考虑到我正在寻找解决问题的合适方法,而不是简单的代码建议,我认为包含足够的细节是很重要的


一般说明: 我正在从事一个项目,该项目要求能够以相对重复的时间间隔“安排”任务

这些间隔是以一些内部时间表示的,这些时间表示为一个整数,随着程序的执行而递增(因此不等于实时)。每次发生这种情况时,将对计划进行互选,以检查在此时间步执行的任何任务

如果执行了任务,则应重新安排该任务在相对于当前时间的位置再次运行(例如,在5个时间步内)。此相对位置仅存储为任务对象的整数属性

问题是: 我正在努力决定我应该如何构造它——部分原因是它是一组稍微难以查找的搜索词

目前,我认为每次定时器增加时,我需要:

  • 在计划中的“0”位置执行任务
  • 在其相对位置将这些任务重新添加到计划中(例如,每5步重复一次的任务将返回到位置5)
  • 计划中的每组任务的“执行前时间”将递减一(例如,位置1处的任务将移至位置0)
  • 假设: 有几个假设可能会限制我可以使用的可能解决方案:

    • 间隔必须是相对的,而不是特定的时间,并且定义为距当前时间的整数步数
    • 这些间隔可以采用任何整数值,例如,没有边界
    • 可以为同一时间步安排多个任务,但它们的执行顺序并不重要
    • 所有执行都应保持在单线程中-由于其他限制,多线程解决方案不适合
    我的主要问题是:

    我怎样才能设计出有效的时间表?哪些数据类型/集合可能有用

    是否有另一种结构/方法我应该考虑? 我不考虑调度框架(例如Quartz)是不是错了,因为它似乎在“实时”时域而不是“非实时”时域中工作得更多



    非常感谢任何可能的帮助。如有必要,请随时发表评论以获取更多信息,我将根据需要进行编辑

    循环链表可能就是您要查找的数据结构。您只需增加循环任务列表中“当前”字段的索引,而不是减少每个任务元素中的字段。伪代码结构可能如下所示:

    tick():
        current = current.next()
        for task : current.tasklist():
            task.execute()
    

    任何时候你安排一个新任务,你只需将它添加到当前“勾号”前面的N个勾号位置。

    好吧,Quartz是非常强大的工具,但是它的配置可能性有限,所以如果你需要特定的功能,你应该编写自己的解决方案

    但是,研究Quartz源代码和数据结构是一个好主意,因为它们成功地解决了许多问题,例如数据库级别的进程间同步、运行延迟任务等


    我曾经写过一次我自己的调度程序,它适用于propably Quartz不容易适应的任务,但一旦我学习了Quartz,我就明白了我可以在解决方案中改进多少,知道它是如何在Quartz中完成的。

    这个怎么样,它使用您自己的标记和ExecuteInterval()

    使用一个。它拥有你所需要的一切。下面是使用它的简单程度:

    // Create a single-threaded ScheduledExecutorService
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); // 1 thread
    
    // Schedule something to run in 10 seconds time 
    scheduler.schedule(new Runnable() {
        public void run() {
            // Do something
        }}, 10, TimeUnit.SECONDS);
    
    // Schedule something to run in 2 hours time 
    scheduler.schedule(new Runnable() {
        public void run() {
            // Do something else
        }}, 2, TimeUnit.HOURS);
    
    // etc as you need
    

    以下是一些想法:

    保持一切简单。如果您没有数以百万计的任务,那么就不需要优化数据结构(除了自豪感或过早优化的冲动)

    避免相对时间。使用绝对内部刻度。如果添加任务,请将“下次运行”设置为当前刻度值。将其添加到列表中,按时间对列表进行排序


    寻找任务时,从列表的最前面开始,挑选所有有时间的任务看看
    DelayQueue
    使用
    PriorityQueue
    来维护这样一个有序的事件列表的方式
    DelayQueue
    使用实时工作,因此可以使用
    条件
    锁支持
    中提供的可变定时等待方法。您可以实现类似于
    SyntheticDelayQueue
    的东西,其行为方式与
    DelayQueue
    相同,但使用您自己的合成时间服务。显然,您必须用jdk替换免费提供的定时等待/信令机制,而这可能是非常重要的,因为要有效地做到这一点。

    如果必须这样做,我会创建一个简单的队列(链表变体)。此队列将包含一个动态数据结构(例如简单列表),其中包含需要完成的所有任务。在每个时间间隔(或时间步),进程读取队列的第一个节点,执行它在该节点列表中找到的指令。在每次执行结束时,它将计算重新调度,并将新的执行添加到队列中的另一个节点,或者在将指令存储到该节点之前创建到达该位置的节点。然后移除第一个节点,并在下一时间步执行第二个节点(现在是第一个)。该系统也不需要跟踪任何整数,所需的所有数据结构都可以在java语言中找到。这应该可以解决您的问题。

    在我看来,您希望通过计数器在应用程序中模拟时间。我想知道您为什么不想使用系统时间,即实时?当然,您可以使用quartz实现这一点。您以前使用过quartz吗。@user384706-我正在模拟现场工作
    import junit.framework.Assert;
    
    import org.junit.Test;
    
    public class TestScheduler {
        private static class Task implements Runnable {
            public boolean didRun = false;
            public void run() {
                didRun = true;
            }       
        }
        Runnable fail = new Runnable() {
            @Override
            public void run() {
                Assert.fail();
            }
        };
    
        @Test
        public void queue() {
            Scheduler scheduler = new Scheduler();
            Task task = new Task();
            scheduler.addTask(task, 0);
            scheduler.addTask(fail, 1);
            Assert.assertFalse(task.didRun);
            scheduler.executeNextInterval();
            Assert.assertTrue(task.didRun);
        }
        @Test
        public void queueWithGaps() {
            Scheduler scheduler = new Scheduler();
            scheduler.addTask(fail, 1);
            scheduler.executeNextInterval();
        }
        @Test
        public void queueLonger() {
            Scheduler scheduler = new Scheduler();
            Task task0 = new Task();
            scheduler.addTask(task0, 1);
            Task task1 = new Task();
            scheduler.addTask(task1, 1);
            scheduler.addTask(fail, 2);
            scheduler.executeNextInterval();
            scheduler.executeNextInterval();
            Assert.assertTrue(task0.didRun);
            Assert.assertTrue(task1.didRun);
        }
    }
    
    // Create a single-threaded ScheduledExecutorService
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); // 1 thread
    
    // Schedule something to run in 10 seconds time 
    scheduler.schedule(new Runnable() {
        public void run() {
            // Do something
        }}, 10, TimeUnit.SECONDS);
    
    // Schedule something to run in 2 hours time 
    scheduler.schedule(new Runnable() {
        public void run() {
            // Do something else
        }}, 2, TimeUnit.HOURS);
    
    // etc as you need