定时任务Timer源码解读

下面是一个Timer 的定时任务demo主要从两个方面进行讲解:

1、创建一个线程来执行定时任务

Timer timer new Timer()  

2、添加定时任务

timer.schedule(task, date, 10000)
public class TimerDemo {
    private static Timer timer = new Timer();//启动一个线程,run方法是个死循环 如 mainLoop所示:


    public static class MyTask extends TimerTask {

        @Override
        public void run() {
            System.out.println("要运行的定时任务执行了!!!!" + new Date());
        }
    }

    public static class MyTask1 extends TimerTask {

        @Override
        public void run() {
            System.out.println("要运行的定时任务执行了!!!!" + new Date());
        }
    }

    public static void main(String[] args) {
        try {
            MyTask task = new MyTask();
            MyTask1 task1 = new MyTask1();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String dateString = "2018-5-5 18:59:00";
            Date date = sdf.parse(dateString);

            String dateString1 = "2018-5-6 19:35:00";
            Date date1 = sdf.parse(dateString1);
            System.out.println("字符串时间:" + date.toLocaleString() + "当前时间:" + new Date().toLocaleString());
            timer.schedule(task, date, 10000);//此方法是把任务添加到排队队列中
//            timer.schedule(task1, date1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

mainLoop详细源码如下:从队列中取出任务执行

private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                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(//这里是需要重复循环执行的任务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) { 

//如果任务抛出异常,程序就结束了,不会再执行任务了

 } } }}

把任务插入到排队队列中:

private void sched(TimerTask task, long time, long period) {
    if (time < 0)
        throw new IllegalArgumentException("Illegal execution time.");

    // Constrain value of period sufficiently to prevent numeric
    // overflow while still being effectively infinitely large.
    if (Math.abs(period) > (Long.MAX_VALUE >> 1))
        period >>= 1;

    synchronized(queue) {
        if (!thread.newTasksMayBeScheduled)
            throw new IllegalStateException("Timer already cancelled.");

        synchronized(task.lock) {
            if (task.state != TimerTask.VIRGIN)
                throw new IllegalStateException(
                    "Task already scheduled or cancelled");
            task.nextExecutionTime = time;
            task.period = period;
            task.state = TimerTask.SCHEDULED;
        }

        queue.add(task);//把任务插入到排队队列中 然后进行排序
        if (queue.getMin() == task)//取出小顶堆,也就是最近要执行的任务
            queue.notify();//唤醒等待线程 就是 mainLoop方法中的 queue.notify()
    }
}

在queue.add(task) 方法中,当添加一个对象时维护了一个小顶堆,每次取出来的是当前最近要执行的任务

private void fixUp(int k) {
    while (k > 1) {
        int j = k >> 1;
        if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
            break;
        TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
        k = j;
    }
}

在mainLoop方法中,当一个任务完成后需要移除,需要重新维护小顶堆

private void fixDown(int k) {
    int j;
    while ((j = k << 1) <= size && j > 0) {
        if (j < size &&
            queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
            j++; // j indexes smallest kid
        if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
            break;
        TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
        k = j;
    }
}
1、Timer:
TimerTask是按nextExecutionTime进行堆排序的。每次取堆中nextExecutionTime和当前系统时间进行比较,如果当前时间大于nextExecutionTime则执行,如果是单次任务,会将任务从最小堆,移除。否则,更新nextExecutionTime的值
至此,Timer定时任务原理基本理解,单线程 + 最小堆 + 不断轮询
缺点:每一个任务都需要占用一个线程资源,而且任务抛异常出去后,定时任务下次就不会在执行了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值