实施一个基于任务的" Java中的程序,不使用时钟

时间:2014-11-17 19:33:16

标签: java algorithm design-patterns scheduled-tasks

我的一位朋友在面试时被要求让Java开发人员实施一个接收任务的程序,这些程序基本上都是一个具有"做什么的对象。方法和表示秒的时间字段(比如整数)。该计划应该执行"做"任务的方法 - 从任务到达程序的那一刻起X秒(其中X是在此任务对象中定义的时间作为时间字段)。

例如,如果程序收到了一个具有"做"的任务。打印的方法"你好我是一个任务"并且有一个时间字段为20,然后在该程序中收到该任务后20分钟 - 一个"你好我是一个任务"消息将打印到consol。

你不能使用时钟或计时器,但你确实有一些"内置调度程序"每秒运行一次,可以检查每个任务的状态,并在需要时执行它们。

我认为一个好的解决方案是调度程序将从每个"任务时间减去一个"如果该时间等于0,则调度程序将执行它并将其从任务列表中删除。 问题在于,如果是长任务列表,这可能需要很长时间才能执行,直到调度程序最终完成所有任务 - 时间不准确。

从我的理解这是一个建模问题,所以它可能与某些设计模式或类似的东西有关。

有没有人知道这个问题的优秀可选解决方案是什么? 谢谢你们......

3 个答案:

答案 0 :(得分:4)

如果这是一个面试问题,那么它很可能会进入排序和数据结构的方向。

首先,从整个时钟滴答和调度程序中抽象出来。这不是问题。它会记录每个时间间隔(比如秒),所以每一秒你都需要找出要执行的任务。

所以你实际上需要一个数据结构,对于传递秒的x,你可以找出要执行的任务。你还需要什么?问题是&#34;接收任务&#34;所以你必须也能够在某个点y插入新的对象。能够删除已执行的任务也是合理的。接下来,我认为仅检查等式t == x是明智的,因为可能是执行时间间隔更长。如果在执行时删除了执行的任务,那么您可以使用t <= x安全地完成所有任务。

总而言之,您需要执行以下操作(我假设时间为长整数):

  • insertTask(int time, Task t)
  • Collection<Task> getTasksScheduledBefore(int time)
  • removeTasksScheduledBefore(t)

应该使用什么?这个问题的答案取决于你的面试地点。 :)

最简单的方法是使用TreeMap&gt;:

之类的东西
    使用insertTask
  • put是微不足道的
  • getTasksScheduledBefore - headMap(t).values()
  • removeTasksScheduledBefore - headMap(t).clear()

如果你正在为谷歌和公司采访,那么他们可能会想出一些会迫使你发明自己的数据结构的东西。树在这里很好,但有一些假设,你也可以用数组,链表等做技巧。 例如,如果您只需提前一天计划,Set<Task>[86400]也可以。 :)

Google也会注意整数溢出等问题。您可能需要使用BigInteger而不是long。确保你与面试官一起检查你的假设(就像那个时间实际上是整数,你应该如何对无效值做出反应等)。

答案 1 :(得分:2)

首先,将开始时间存储在变量中。在调度程序的每个滴答处,将其递增1。

当安排新任务时,将其包装在存储任务的对象和执行该任务的滴答中(currentTick +任务计划时间)。将包装器放在一个列表中,然后按开始时间对列表进行排序(或立即将其放在正确的位置)。

然后,在每个刻度线上,您只需检查列表中的第一个任务以查看是否应该执行该任务。如果是,请检查下一个,否则,您已完成。

这样,添加新任务需要花费更多的精力,但是你最大限度地减少了调度程序的每个滴答所需的工作(这将更频繁地发生)。

答案 2 :(得分:1)

好像我和David ten Hove有同样的想法。我使用地图todos并将预定时间作为关键字,因此我不需要对其进行排序,只需检查它是否包含当前时间。

currentCounter初始化为0并且每秒递增一次(感谢计划)。我们不需要知道实际的当前时间和提到的问题的主题&#34;没有时钟&#34;。

package org.conffusion.taskmgr;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Taskmanager {

    /**
     * map of scheduled tasks. For a single moment in time multiple tasks can be
     * scheduled, so a Collection structure of Tasks is necessary.
     */
    private Map<Long, Collection<Task>> todos = new HashMap<Long, Collection<Task>>();

    /**
     * increased every second since the startup of the Manager
     */
    private static long currentCounter = 0;

    /**
     * Use this method to add a new task to the manager.
     * @param task
     */
    public void addTask(Task task) {
        long key=currentCounter + task.getDelay();
        if(todos.containsKey(key))
        {
            // there is already a task for this time, so just add it to the existing list.
            todos.get(key).add(task);
        } else
        {
            List<Task> tasklist=new ArrayList<Task>();
            tasklist.add(task);
            todos.put(key, tasklist);
        }
    }

    /**
     * called by the scheduler every second
     */
    public void schedulerCallback() {
        currentCounter++;
        // Do we have tasks for the current time ?
        if(todos.containsKey(currentCounter))
        {
            // Loop over the scheduled tasks and execute them.
            for(Task t:todos.get(currentCounter))
            {
                // Run every task in a separate thread so our manager does not get blocked by the tasks.
                new Thread(new TaskRunner(t)).start();
            }
            // Clean up of launched tasks
            todos.remove(currentCounter);
        }
    }

    private class TaskRunner implements Runnable {
        private Task task;
        public TaskRunner(Task task)
        {
            this.task=task;
        }
        @Override
        public void run() {
            task.todo();
        }   
    }
    /**
     * Interface every Task must implement.
     */
    public interface Task {

        /**
         * Delay in seconds to wait before todo() must be called.
         * @return
         */
        public long getDelay();

        public void todo();
    }
}