java定时任务实现原理及扩展思路-----Timer

java定时任务实现原理-----Timer

近期在项目中使用到了java的定时任务, 但是是非固定周期的重复性定时任务, 于是想对最基础的Timer实现原理探究一下.

定时任务需要哪些组件?

大致我们能够想到, 一个定时任务至少需要三个组件:
1. 承载业务的对象 ----- 被调度对象
2. 存储容器 ----- 暂时存储被调度对象, 这个容器内的元素应该是有顺序的,
3. 调度器 ------ 管理存储容器, 通过限时进入等待与被唤醒实现定时

在java中, 最简单的定时任务实现就是Timer.简单Demo如下:

public static void main(String[] args) {
        TimerTask task1 = new TimerTask() {
            @Override
            public void run() {
                System.out.println("延迟10s");
                System.out.println(System.currentTimeMillis());

                //结束定时器
                throw new RuntimeException();
            }
        };

        Timer timer = new Timer();
        timer.schedule(task1, 10000);

        System.out.println(System.currentTimeMillis());
    }

TimerTask 承载业务, 其中定义了几个状态,防止但实例被多线程调度

Timer中聚合了 TaskQueue和TimerThread.
TaskQueue 储存TimerTask, 内部存储的元素具有有序, 通过TaskQueue#getMin获取延迟时间最小的任务
TimerThread 主要完成调度工作, 主要代码如下:

private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                //1. 因为TaskQueue内部没有实现线程安全, 所以对queue的修改以及查询都需要加锁
                //2. 当TimerThread在queue实例上等待的时候, 调用queue对象的的notify可以唤醒
                //    TimerTask
                synchronized(queue) {
                    // Wait for queue to become non-empty
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        //队列为空和当前线程依然需要存活, 就继续等待
                        queue.wait();
                    if (queue.isEmpty())
                        break; // Queue is empty and will forever remain; die

                    // Queue nonempty; look at first evt and do the right thing
                    long currentTime, executionTime;
                    //获取距离当前最近的一个任务
                    task = queue.getMin();
                    synchronized(task.lock) {
                        //对于已经取消的任务,直接忽略即可
                        if (task.state == TimerTask.CANCELLED) {
                            queue.removeMin();
                            continue;  // No action required, poll queue again
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;
                        //如果拿到的任务预计执行时刻已经过了, 那么后续将会立即执行
                        if (taskFired = (executionTime<=currentTime)) {
                            //如果是非重复性任务
                            if (task.period == 0) { // Non-repeating, remove
                                //非重复性需要从队列里面移除任务
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { // Repeating task, reschedule
                                //重复性任务只需要调整这个实例在队列中的位置
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    //如果任务的预执行时刻还未到, 则当前线程进入限时等待.
                    if (!taskFired) // Task hasn't yet fired; wait
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks
                    task.run();
            } catch(InterruptedException e) {
            }
        }
    }
1. TimerTask 启动之后就会进入等待, 直到有其他线程唤醒为止.
2. TimerTask 被唤醒之后, 拿到任务, 如果是周期性任务, 就调整任务的下一次执行时间,
 如果是非周期性任务, 直接删除即可(这样也就是延时任务与周期性任务的实现)
3. 如果还没到任务预定的执行时刻, TimerThread 会进入限时等待状态, 后续有其他线程调用schedul()这类方法, 
  有机会使TimerTask提前被唤醒, 如果被唤醒, 那么将进入下一次循环.(这个使得先提交延时长的任务, 后提交延时短
  的任务, 但是依然能按照预定延时实现)

Timer的弊端:

  1. 单线程不可靠
  2. 对于重复执行的任务, 周期只能固定
  3. 重复执行的任务, 重复的次数不好控制, 不好实现有条件的重复

扩展思路:

  1. 单线程不可靠如何扩展?
    可以使用线程池来完成调度
  2. 重复性任务周期固定?
    首先需要扩展TimerTask, 增加一个getPeriod()方法, 这个方法的返回值可以随着当前实例被调用次数而改变,
    这样即可实现自定义周期扩展

如上代码, 使得重复性任务只能以固定周期执行. 如果将 task.period 调用该为 task.getPeriod(),
我们就可以通过扩展Timer实现getPeriod()方法使得:

  • 不固定周期的重复性任务
  • 可控制重复次数的重复任务
  • 有条件的重复任务
  • 完全无规律就按照给的时间执行的重复任务
    ...
posted @ 2018-11-13 23:21  index_1  阅读(1427)  评论(0编辑  收藏  举报