aleung
5/27/2013 - 10:48 AM

A simple Java scheduler, which execute scheduled tasks in current thread.

A simple Java scheduler, which execute scheduled tasks in current thread.

package leoliang.common;

import com.google.common.base.Preconditions;

/**
 * Usage example: Run 15 times in every 250ms, begins at 5 seconds later.
 *
 * <pre>
           Scheduler.every(250, Scheduler.TimeUnit.MILLISECOND)
                .beginsAt(System.currentTimeMillis() + 5000)
                .times(15)
                .execute(new Runnable() {
                    public void run() {
                        // do something
                    }
                });

 * </pre>
 */
public class Scheduler {

    public enum TimeUnit {
        MILLISECOND(1), SECOND(1000), MINUTE(1000 * 60);

        public final long milliseconds;

        private TimeUnit(long milliseconds) {
            this.milliseconds = milliseconds;
        }
    }

    private long firstExecutionMillisecond;
    private long intervalMillisecond;
    private long stopsBeforeMillisecond = Long.MAX_VALUE;
    private int executionTimes = Integer.MAX_VALUE;

    protected Scheduler() {
        // nothing to do
    }

    public static Scheduler every(long interval, TimeUnit unit) {
        Scheduler scheduler = new Scheduler();
        scheduler.intervalMillisecond = interval * unit.milliseconds;
        return scheduler;
    }

    public static long millisecondOnNextSecond() {
        long current = System.currentTimeMillis();
        return (current / 1000) * 1000;
    }

    /**
     * Set the first execution time. If not set, first execution will be at the time that {@link #execute(Runnable)} is
     * called.
     *
     * @param millisecond
     * @return
     */
    public Scheduler beginsAt(long millisecond) {
        firstExecutionMillisecond = millisecond;
        return this;
    }

    /**
     * Limit that last execution should before a specific moment.
     *
     * @param executionTimes
     * @return
     */
    public Scheduler stopsBefore(long millisecond) {
        stopsBeforeMillisecond = millisecond;
        return this;
    }

    /**
     * Set the total times of execution.
     *
     * @param executionTimes
     * @return
     */
    public Scheduler times(int executionTimes) {
        this.executionTimes = executionTimes;
        return this;
    }

    /**
     * Execute the task as schedule. The execution is in current thread. Missing schedule will be skipped.
     *
     * @param scheduleTask
     */
    public void execute(Runnable task) {
        Preconditions.checkArgument(intervalMillisecond > 0, "Interval is not set.");

        try {
            long nextFireMillisecond;

            if (firstExecutionMillisecond > 0) {
                fireAt(firstExecutionMillisecond, task);
                nextFireMillisecond = firstExecutionMillisecond + intervalMillisecond;
            } else {
                nextFireMillisecond = System.currentTimeMillis() + intervalMillisecond;
                task.run();
            }

            int executedTimes = 1;
            while ((executedTimes < executionTimes) && (nextFireMillisecond < stopsBeforeMillisecond)) {
                fireAt(nextFireMillisecond, task);
                executedTimes++;
                nextFireMillisecond += intervalMillisecond;
            }
        } catch (InterruptedException e) {
            return;
        }
    }

    private void fireAt(long millisecond, Runnable task) throws InterruptedException {
        long sleepMillis = millisecond - System.currentTimeMillis();
        if (sleepMillis < 0) {
            return; // schedule missed, skip
        }
        if (sleepMillis > 0) {
            Thread.sleep(sleepMillis);
        }
        task.run();
    }

}