关于Timer的几点认识

作者: tmp_zhao | 来源:发表于2016-09-11 11:10 被阅读219次

    缘起

    我们的代码里用到了Timer,差不多是这样:

    Timer timer = new Timer("xxx");
    timer.schedule(task, 0, 20);
    

    每20ms执行一次task,这里的task并不是耗时操作,基本在1ms左右完成,但是在某些6.x设备上的调度结果显示,差不多是60ms才执行一次,和指定的20ms相差很远啊!!!这么神奇的现象于我来说怎么会放过呢,于是乎点进去研究了发源码。下面主要说下几个关键的结论,供参考。

    结论1

    每个Timer背后对应一个线程,n多个TimerTask都会在这个timer实例对应的线程中执行。其中只要有某一个task的run方法抛了异常,那么会导致整个timer的thread提前结束,后面所有的任务都不会被执行;坑!!!

    结论2

    Timer对调度的支持是基于绝对时间,即实现里用到了System.currentTimeMillis(),而不是相对时间的,因此任务对系统时钟的改变是敏感的,而ScheduledThreadPoolExecutor只支持相对时间;

    结论3

    重复执行的schedule方法是相对上次task执行完的时间,比如period是10s,现在时间是9:00:10,假设上一个task执行完是50s,那么得到9:01:00的时候,第2个task才会被调度;可以看出在fixed-delay模式下,下一个task什么时候被调度取决于上一个task什么时候执行完,这样就造成了时效准确性问题;
    * In fixed-delay execution, each execution is scheduled relative to
    * the actual execution time of the previous execution. If an execution
    * is delayed for any reason (such as garbage collection or other
    * background activity), subsequent executions will be delayed as well.

    结论4

    重复执行的scheduleAtFixedRate方式是相对上次task开始执行的时间(被调度的时间),所以各个task开始执行的时间都是固定的,但因为所有task需要在同一个线程中执行,所以会存在比如某次task执行的时间比较久,导致当前时间超过了接下来2、3个task的调度时间,就会出现连续快速的调度现象,并不是你想象的间隔period,因为只有这样才能确保整体平均下来确实是按照fixed-rated调度的;

    结论5

    在Java5或更高的版本中,几乎没有任何理由再使用Timer了,请使用ScheduledThreadPoolExecutor代替,参考《Java并发编程实践6.2.5节》。

    相关文章

      网友评论

      • 飞翔的小鱼hao: @tmp_zhao 谢谢。正在看ScheduleThreadPoolExecutor 类
      • 飞翔的小鱼hao:哦,那淘汰了。用什么类实现定时任务
        tmp_zhao:java.util.concurrent.Executors里面有很多工厂方法,可以返回各种需要的ThreadPoolExecutor,都可以看看。
        tmp_zhao:@飞翔的小鱼hao 用ScheduledThreadPoolExecutor代替,比如:
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
        scheduledExecutorService.scheduleAtFixedRate(command, 0, 20, TimeUnit.MILLISECONDS);

      本文标题:关于Timer的几点认识

      本文链接:https://www.haomeiwen.com/subject/voduettx.html