Timer源码分析

前言

Timer是个轻量级的定时控件,一般项目中不太常见,对于普通业务一般可以用handler或者alarmmaager来实现,但是最近研究thread。就看了下Timer类。忽然发现设计也是很优秀的。这里就对这个控件进行了分析。

用法

Timer主要是针对定时刷新等业务的一种简单实现,一般代码如下:

TimerTask timerTask = new TimerTask() {
     @Override
     public void run() {
          Log.v(">>>>>", "it happen");
          Message message = new Message();
          message.what = 1;
          handler.sendMessage(message);
     }
};
time = System.currentTimeMillis();
Timer timer = new Timer(true);
timer.schedule(timerTask, 10000, 10000); //延时1000ms后执行,1000ms执行一次
//timer.cancel();  这是取消定时器的指令

使用方法非常简单。系统仅仅提供了三个基本控制函数
1. schedule()这是设置task的函数这里有五个变种,就不在细说。一会源码分析会有。
2. cancel()是取消所有的task,注意这里不会影响当前正在运行的task。
3. purge()移除所有任务。
这里功能基本费心完成。我们来研究源码

源码分析

在研究源码时候,基本都是从构造函数入手。这里我们来看看构造函数。

public Timer() {  this(false); }   //调用了自身的构造函数

public Timer(boolean isDaemon) {
    this("Timer-" + Timer.nextId(), isDaemon);  //再次调用自己构造函数
}

//这是最重要的一个构造函数。里面name线程名字。isDaemon是代表timer所在的线程是否是守护进程。前两个默认的的是非守护进程。getname()函数是获取的是一个唯一的序列号。
public Timer(String name, boolean isDaemon) {  
    if (name == null) {
        throw new NullPointerException("name == null");
    }
    this.impl = new TimerImpl(name, isDaemon);
    this.finalizer = new FinalizerHelper(impl);
}

这里我们立马就会发现两个全局变量imp和finalizer他们是有干什么的那么我们就开始看他的源码。这里首先看个简单的,一个静态内部类。

    private static final class FinalizerHelper {
        private final TimerImpl impl;

        FinalizerHelper(TimerImpl impl) {
            this.impl = impl;
        }

        @Override protected void finalize() throws Throwable {
            try {
                synchronized (impl) {
                    impl.finished = true;
                    impl.notify();
                }
            } finally {
                super.finalize();
            }
        }
    }

这个类很有意思。他仅仅是在自己被垃圾回收机制给回收前给imp的finshed给设成true,(这里我有个疑问为啥不直接在timer类中复写finalize()方法,ps有个大神给我说是面向对象的单一职责原则,貌似一定意义上说的通)。
实例化完成后我们本来应该看TimerImpl类的,但是有点复杂,我们就跟踪这个timer流程来走schedule()函数。这里所有的schedule()函数都指向这一个函数。下面我们来分析下。

    private void scheduleImpl(TimerTask task, long delay, long period, boolean fixed) {
        synchronized (impl) {
            if (impl.cancelled) {
                throw new IllegalStateException("Timer was canceled");
            }

            long when = delay + System.currentTimeMillis();

            if (when < 0) {
                throw new IllegalArgumentException("Illegal delay to start the TimerTask: " + when);
            }

                synchronized (task.lock) { //这里检车是否已经存在任务中了。不还重点。总之这一切都是为了安全
                if (task.isScheduled()) {
                    throw new IllegalStateException("TimerTask is scheduled already");
                }

                if (task.cancelled) {
                    throw new IllegalStateException("TimerTask is canceled");
                }

                task.when = when;
                task.period = period;
                task.fixedRate = fixed;
            }

            // insert the newTask into queue 
            //这里是重点。所有的任务表明这里仅仅是让给imp来处理。imp是implement的简写,是实现类
            impl.insertTask(task);
        }
    }

这里我们先放下impl.insertTask(task);。开始研究task

public abstract class TimerTask implements Runnable {
      final Object lock = new Object();

      /* If timer was cancelled */
      boolean cancelled;

      //什么时候开启
      long when;

      long period;

      boolean fixedRate;

    ......

    public abstract void run();

这里几个函数主要是主要是为了获取参数和异步访问安全的问题。主要知道这几个参数,最重要是实现这个run()方法,主要的事件是在这里处理的。这里了解了TimerTask 。那么就开始这个最难啃的TimerImpl这个timer的具体实现类。
首先构造函数

TimerImpl(String name, boolean isDaemon) {
    this.setName(name);   //使用线程必须有name
    this.setDaemon(isDaemon);  //必须设置。
    this.start();  开始了当前的线程。
}

这里的TimerImpl是一个thread。真正的方法都在threadimp中运行,这也就是我们知道的为啥不能在timer中改变ui的原因了。接下类我们看run()方法:

    @Override
    public void run() {
            while (true) {
                TimerTask task;
                synchronized (this) {
                    // need to check cancelled inside the synchronized block
                    if (cancelled) {
                        return;
                    }
                    if (tasks.isEmpty()) {
                        if (finished) {
                            return;
                        }
                        // no tasks scheduled -- sleep until any task appear 
                        //没有消息就等待
                        try {
                            this.wait();
                        } catch (InterruptedException ignored) {
                        }
                        continue;
                    }

                    long currentTime = System.currentTimeMillis();

                    task = tasks.minimum();
                    long timeToSleep;
            //消息取消了就直接删除
                    synchronized (task.lock) {
                        if (task.cancelled) {
                            tasks.delete(0);
                            continue;
                        }

                        // check the time to sleep for the first task scheduled
                        timeToSleep = task.when - currentTime;
                    }

                    if (timeToSleep > 0) {
                        // sleep!
                        try {
                            this.wait(timeToSleep);
                        } catch (InterruptedException ignored) {
                        }
                        continue;
                    }

                    // no sleep is necessary before launching the task
                    //到这里我也看得不太懂前半分是为了验证task的正确性。后半分是在添加吓一跳task
                    synchronized (task.lock) {
                        int pos = 0;
                        //这里感觉不太会自己when和自己不相等。感觉是为了安全监测
                        if (tasks.minimum().when != task.when) {
                            pos = tasks.getTask(task);
                        }
                        //又一次监测
                        if (task.cancelled) {
                            tasks.delete(tasks.getTask(task));
                            continue;
                        }

                        // set time to schedul
                        task.setScheduledTime(task.when);

                        // remove task from queue
                        //删除当前的task、
                        tasks.delete(pos);

                        // set when the next task should be launched
                        //貌似是为了设置下个一个task
                        if (task.period >= 0) {
                            // this is a repeating task,
                            if (task.fixedRate) {
                                // task is scheduled at fixed rate
                                task.when = task.when + task.period;
                            } else {
                                // task is scheduled at fixed delay
                                task.when = System.currentTimeMillis()
                                        + task.period;
                            }

                            // insert this task into queue
                            insertTask(task);
                        } else {
                            task.when = 0;
                        }
                    }
                }

                boolean taskCompletedNormally = false;
                try {
                 //在这里执行代码。这里我们仅仅是调用了run方法。还是在timerImp的thread中执行
                    task.run();
                    taskCompletedNormally = true;
                } finally {
                    if (!taskCompletedNormally) {
                        synchronized (this) {
                            cancelled = true;
                        }
                    }
                }
            }
        }

这里是run()方法,里面我只要知道timerImp中有一个用来保存task的全局变量tasks,这个类可以自动对task进行对时间的排序。和添加任务。这里我们从开始往下看主意英文注释。前面很好理解。到了后面我给出了注释,在task.run()之前都是安全检测与等待,这里就是主要的内容。在这里我们看到了梦想中task的内容被调用,这里我在分析下消息对列tasks的添加吧。调用是在之前看到的
java impl.insertTask(task);
这里找到方法是

        private void insertTask(TimerTask newTask) {
            // callers are synchronized
            //这是关键。
            tasks.insert(newTask);
            //这里是让线程在sleep中唤醒
            this.notify();
        }

我们找打tasks的TimerHeap类中insert方法

public void insert(TimerTask task) {
    //开始不会进入因为是为了timers的长度达到最大值时的处理方法
     if (timers.length == size) {
           TimerTask[] appendedTimers = new TimerTask[size * 2];
           system.arraycopy(timers, 0, appendedTimers, 0, size);
           timers = appendedTimers;
      }
      //这里就是添加task
      timers[size++] = task;
      //排序,这里我就不在写了。有兴趣看以看看。
      upHeap();
}

至此timer类完成。仅仅500多行的代码写了这么多。但是里面确实是一个小型的消息处理机制。很有学习价值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值