zoukankan      html  css  js  c++  java
  • 好好的Timer居然有坑?

    在做定时任务时,可能会使用到Timer+TimerTask类,但是这两个小小的类,却有大坑。

    先来复现一下问题,如下,可能预期的是第一个PrintTask从1一直往后打印,直到为5时抛出异常,第二个PrintTask从100往后不间断打印。

    public class Main12 {
        public static void main(String[] args) {
            Timer timer =new Timer();
            timer.schedule(new PrintTask(1),0,1000);
            timer.schedule(new PrintTask(100),0,1000);
        }
        static class PrintTask extends java.util.TimerTask{
            private  int mStart;
    
            public PrintTask(int start) {
                mStart = start;
            }
    
            @Override
            public void run() {
                if (mStart==5){
                   throw  new RuntimeException();
                }
                System.out.println("mStart=="+mStart++);
            }
        }
    }
    mStart==1
    mStart==100
    mStart==101
    mStart==2
    mStart==3
    mStart==102
    mStart==4
    mStart==103
    Exception in thread "Timer-0" java.lang.RuntimeException
    	at main.Main12$PrintTask.run(Main12.java:21)
    	at java.util.TimerThread.mainLoop(Timer.java:555)
    	at java.util.TimerThread.run(Timer.java:505)
    
    Process finished with exit code 0
    
    

    但是结果却是,在第一个PrintTask到5抛出异常后,第二个PrintTask却奇迹般的终止了。
    这里是不是说明某个TimerTask中的run方法向外抛出异常后,其他TimerTask会自动终止呢?

    那就得看Timer的实现原理了。

    Timer实现原理

    在这里插入图片描述
    Timer中维护这一个TaskQueue和一个TimerThread,TaskQueue是一个由平衡二叉树实现的优先级队列,在调用Timer的schedule方法时,也就是把TimerTask放入TaskQueue中。TimerThread则是具体执行任务的线程,它从TaskQueue中获取优先级最高的任务去执行,并且只有当前任务执行完后才会从队列中获取下一个任务,不管队列里是否有任务已经到了设置的delay时间。这个模型也就是多生产者---单消费者(只要这个消费者线程挂了,其他任务就没办法执行)。

    所以,重中之重在于TimerThread,只需要搞明白TimerThread就可以了。

    在TimerThread的mainLoop方法中开始获取任务并执行,当任务抛出InterruptedException以外的异常时,唯一的消费者线程就会因异常而终止,队列里的其他任务就会被清除。

    public void run() {
        try {
            mainLoop();
        } finally {
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  
            }
        }
    }
      private void mainLoop() {
          while (true) {
              try {
                  TimerTask task;
                  boolean taskFired;
                  //从队列里面获取任务时加锁。
                  synchronized(queue) {
                  	......
                  }
                  if (taskFired)  
                      task.run(); //执行任务
              } catch(InterruptedException e) {
              }
          }
    

    所以在TimerTask的run方法中,最好使用try-catch结构捕捉异常,不要向上抛。

    还可以使用ScheduledThreadPoolExecutor,ScheduledThreadPoolExecutor中的一个任务抛出异常,其他任务也不受影响。

  • 相关阅读:
    怎样使android的view动画循环弹动
    QQ消息99+形成--第三方开源--BezierView
    自定义Toast的显示效果
    (转载)实现QQ侧滑边栏
    图片加载与缓存利器(自动缓存)--第三方开源-- Glide
    多层级Spinner列表选项实时更新树形层级(选择城市)
    android任意view爆炸效果--第三方开源--ExplosionField
    TextView字符串波浪式跳动--第三方开源---JumpingBeans
    简单Spinner
    【前端】JavaScript入门学习
  • 原文地址:https://www.cnblogs.com/HouXinLin/p/12560054.html
Copyright © 2011-2022 走看看