Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java- Efficient Scheduling Structure?

I apologise for the length of this problem, but I thought it important to include sufficient detail given that I'm looking for a suitable approach to my problem, rather than a simple code suggestion!


General description:

I am working on a project that requires tasks being able to be 'scheduled' at some relative repeating interval.

These intervals are in terms of some internal time, that is represented as an integer that is incremented as the program executes (so not equal to real time). Each time this happens, the schedule will be interogated to check for any tasks due to execute at this timestep.

If a task is executed, it should then be rescheduled to run again at a position relative to the current time (e.g. in 5 timesteps). This relative position is simply stored as an integer property of the Task object.

The problem:

I am struggling somewhat to decide upon how I should structure this- partly because it is a slightly difficult set of search terms to look for.

As it stands, I am thinking that each time the timer is incremented I need to:

  1. Execute tasks at the '0' position in the schedule
  2. Re-add those tasks to the schedule again at their relative position (e.g. a task that repeats every 5 steps will be returned to the position 5)
  3. Each group of tasks in the schedule will have their 'time until execution' decremented one (e.g. a task at position 1 will move to position 0)

Assumptions:

There are a couple of assumptions that may limit the possible solutions I can use:

  • The interval must be relative, not a specific time, and is defined to be an integer number of steps from the current time
  • These intervals may take any integer value, e.g. are not bounded.
  • Multiple tasks may be scheduled for the same timestep, but their order of execution is not important
  • All execution should remain in a single thread- multi-threaded solutions are not suitable due to other constraints

The main questions I have are:

How could I design this Schedule to work in an efficient manner? What datatypes/collections may be useful?

Is there another structure/approach I should consider?

Am I wrong to dismiss scheduling frameworks (e.g. Quartz), which appear to work more in the 'real' time domain rather 'non-real' time domain?


Many thanks for any possible help. Please feel free to comment for further information if neccessary, I will edit wherever needed!

like image 989
obfuscation Avatar asked Sep 09 '11 14:09

obfuscation


2 Answers

Well, Quartz is quite powerfull tools, however it has limited configuration possibilities, so if you need specific features, you should propably write your own solution.

However, it's a good idea to study the Quartz source code and data structures, because they have successfully dealt with much problems you would find f.g. inter-process synchronization on database level, running delayed tasks etc.

I've written once my own scheduler, which was adapted to tasks where propably Quartz would not be easy to adapt, but once I've learned Quartz I've understood how much I could improve in my solutions, knowing how it was done in Quartz.

like image 135
Danubian Sailor Avatar answered Nov 10 '22 06:11

Danubian Sailor


How about this, it uses your own Ticks with executeNextInterval() :

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class Scheduler {
    private LinkedList<Interval> intervals = new LinkedList<Scheduler.Interval>();

    public void addTask(Runnable task, int position) {
        if(position<0){
            throw new IllegalArgumentException();
        }
        while(intervals.size() <= position){
            intervals.add(new Interval());
        }
        Interval interval = intervals.get(position);
        interval.add(task);
    }

    public void executeNextInterval(){
        Interval current = intervals.removeFirst();
        current.run();
    }
    private static class Interval {
        private List<Runnable> tasks = new ArrayList<Runnable>();
        public void add(Runnable task) {
            tasks.add(task);
        }
        public void run() {
            for (Runnable task : tasks) {
                task.run();
            }
        }
    }
}

You might want to add some error handling, but it should do your job.

And here are some UnitTests for it :)

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);
    }
}
like image 1
flob Avatar answered Nov 10 '22 04:11

flob