我的一位朋友在面试时被要求让Java开发人员实施一个接收任务的程序,这些程序基本上都是一个具有"做什么的对象。方法和表示秒的时间字段(比如整数)。该计划应该执行"做"任务的方法 - 从任务到达程序的那一刻起X秒(其中X是在此任务对象中定义的时间作为时间字段)。
例如,如果程序收到了一个具有"做"的任务。打印的方法"你好我是一个任务"并且有一个时间字段为20,然后在该程序中收到该任务后20分钟 - 一个"你好我是一个任务"消息将打印到consol。你不能使用时钟或计时器,但你确实有一些"内置调度程序"每秒运行一次,可以检查每个任务的状态,并在需要时执行它们。
我认为一个好的解决方案是调度程序将从每个"任务时间减去一个"如果该时间等于0,则调度程序将执行它并将其从任务列表中删除。 问题在于,如果是长任务列表,这可能需要很长时间才能执行,直到调度程序最终完成所有任务 - 时间不准确。
从我的理解这是一个建模问题,所以它可能与某些设计模式或类似的东西有关。
有没有人知道这个问题的优秀可选解决方案是什么? 谢谢你们......
答案 0 :(得分:4)
如果这是一个面试问题,那么它很可能会进入排序和数据结构的方向。
首先,从整个时钟滴答和调度程序中抽象出来。这不是问题。它会记录每个时间间隔(比如秒),所以每一秒你都需要找出要执行的任务。
所以你实际上需要一个数据结构,对于传递秒的x
,你可以找出要执行的任务。你还需要什么?问题是"接收任务"所以你必须也能够在某个点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();
}
}