timer 订阅
Timer
信息
操作系统
未知
开发语言
开源协议
未知
Timer
Timer is an extension that is loosely based on Prototype's PeriodicalExecuter . The three main enhancements are: * It's for jQuery (1.1.x and 1.2.x) * Timer intervals can be reset during the event * It's a much shorter name
收起全文
精华内容
参与话题
问答
  • 定时器Timer定时器Timer类 new Timer().schedule(a,b,c) a)表示要发生的事件 b)第一次发生的时间 c)第一次发生后,接下来每隔几秒发生 一次隔三秒或两秒响应事件 public class TraditionTimerClass { public ...

    定时器Timer类定时器Timer类

        new Timer().schedule(a,b,c)
            a)表示要发生的事件
            b)第一次发生的时间
            c)第一次发生后,接下来每隔几秒发生
    一次隔三秒或两秒响应事件
        public class TraditionTimerClass {
    
        public static void main(String[] args) {
    
            new Timer().schedule(new myTimerTask()
    
                , 1000);
    
                }
    
            }
        class myTimerTask extends TimerTask {
    
            private static int count = 0;
    
    
            public void run() {
                count = (count+1)%2;
                System.out.println("BoomHa");
    
                new Timer().schedule(new myTimerTask(),2000+2000*count);
    
            }
    
        }
    
    展开全文
  • 一、Timer 定时器基本使用、 二、Timer 定时器常用用法、 三、Timer 源码分析、 四、Timer 部分源码注释、 五、源码及资源下载



    参考文档 :





    一、Timer 定时器基本使用



    Timer 可用于执行延迟任务或循环任务 ; 下面是定时器最基本用法 ;


    1 . Timer 定时器基本使用 :

    • 创建 Timer 定时器 : 调用构造函数创建定时器 Timer timer = new Timer() ;
    • 分配 TimerTask 定时器任务 : 调用定时器的 schedule 方法 , 为 Timer 定时器分配 TimerTask 定时器任务 ;
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    // ...
                }
            }, 1_000);
    

    2 . 定时器任务执行规则 : Timer 执行任务是 串行执行 的 , 同一时间只能执行一个任务 ;

    在下面的示例中

    • 任务 1 在 1 秒之后执行 , 在第 6 秒执行完毕 ;
    • 任务 2 在第 6 秒 , 任务 1 执行完毕后 , 才开始执行 , 在第 11 秒执行完毕 ;

    3 . 代码示例 :

        private void timer(){
            // Timer 可用于执行延迟任务或循环任务
            Timer timer = new Timer();
    
            /*
                如果提交多个 TimerTask 定时器任务
                需要等待之前的 定时器任务 执行完成 , 才能执行后面的任务
    
                TimerTask 实现了 Runnable 接口
    
                延迟 1 秒执行任务 1 ( 任务 1 时长 5 秒 )
                延迟 2 秒执行任务 2 ( 任务 2 时长 5 秒 )
    
                Timer 执行任务是串行执行的 , 同一时间只能执行一个任务
                任务 1 在 1 秒之后执行 , 在第 6 秒执行完毕
                任务 2 在第 6 秒 , 任务 1 执行完毕后 , 才开始执行 , 在第 11 秒执行完毕
             */
    
            // 延迟 1 秒执行任务 1
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    Log.i(TAG, "延迟 1 秒执行 5 秒的任务 1 开始执行");
                    try {
                        Thread.sleep(5_000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.i(TAG, "延迟 1 秒执行 5 秒的任务 1 执行完毕");
                }
            }, 1_000);
    
            // 延迟 2 秒执行任务 2
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    Log.i(TAG, "延迟 2 秒执行 5 秒的任务 2 开始执行");
                    try {
                        Thread.sleep(5_000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.i(TAG, "延迟 2 秒执行 5 秒的任务 2执行完毕");
                }
            }, 2_000);
        }
    




    二、Timer 定时器常用用法



    1 . Timer 定时器构造函数 :


    ① 创建默认定时器 : 默认以 “Timer-序列号” 作为定时器线程名称 ;

    public Timer() { this("Timer-" + serialNumber()); }
    

    ② 创建守护线程定时器 : 指定定时器是否作为守护线程来执行 , 定时器线程名称是默认名称 ;

    public Timer(boolean isDaemon) { this("Timer-" + serialNumber(), isDaemon); }
    

    ③ 创建定时器并指定定时器名称 : 创建定时器 , 并 设置定时器线程名称 ;

        public Timer(String name) {
            thread.setName(name);
            thread.start();
        }
    

    ④ 指定名称并设置守护线程 : 同时 设置定时器名称 , 并 设置定时器是否是守护线程 ;

        public Timer(String name, boolean isDaemon) {
            thread.setName(name);
            thread.setDaemon(isDaemon);
            thread.start();
        }
    


    2 . 定时器调度方法 :


    ① 在指定一段时间后执行定时器任务 : 在 delay 毫秒后 , 执行 TimerTask 定时器任务 ;

        public void schedule(TimerTask task, long delay) {
            if (delay < 0)
                throw new IllegalArgumentException("Negative delay.");
            sched(task, System.currentTimeMillis()+delay, 0);
        }
    

    ② 在指定的时间执行定时器任务 : 在某个指定的时间执行 TimerTask 定时器任务 ;

    public void schedule(TimerTask task, Date time) { sched(task, time.getTime(), 0); }
    

    ③ 在指定的时间执行循环任务 : 在 firstTime 时间执行第一次 TimerTask 定时器任务 , 之后每隔 period 毫秒的周期时间 , 循环执行定时器任务 ; 循环周期是 period 毫秒 ; 如果因为某种原因导致某些操作出现了延迟 , 那么后续操作也会跟着延迟 ;

        public void schedule(TimerTask task, Date firstTime, long period) {
            if (period <= 0)
                throw new IllegalArgumentException("Non-positive period.");
            sched(task, firstTime.getTime(), -period);
        }
    

    ④ 延迟指定时间循环执行任务 : 延迟 delay 毫秒后 , 执行第一次定时器任务 , 然后每隔 period 毫秒 , 循环执行定时器任务 ; 循环周期是 period 毫秒 ; 如果因为某种原因导致某些操作出现了延迟 , 那么后续操作也会跟着延迟 ;

        public void schedule(TimerTask task, long delay, long period) {
            if (delay < 0)
                throw new IllegalArgumentException("Negative delay.");
            if (period <= 0)
                throw new IllegalArgumentException("Non-positive period.");
            sched(task, System.currentTimeMillis()+delay, -period);
        }
    

    ⑤ 在指定的时间执行循环任务 : 在 firstTime 时间执行第一次 TimerTask 定时器任务 , 之后每隔 period 毫秒的周期时间 , 循环执行定时器任务 ; 循环周期是 period 毫秒 ; 如果因为某种原因导致某些操作出现了延迟 , 那么后续操作需要补偿上述出现的延迟 ;

        public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
            if (delay < 0)
                throw new IllegalArgumentException("Negative delay.");
            if (period <= 0)
                throw new IllegalArgumentException("Non-positive period.");
            sched(task, System.currentTimeMillis()+delay, period);
        }
    

    ⑥ 延迟指定时间循环执行任务 : 延迟 delay 毫秒后 , 执行第一次定时器任务 , 然后每隔 period 毫秒 , 循环执行定时器任务 ; 循环周期是 period 毫秒 ; 如果因为某种原因导致某些操作出现了延迟 , 那么后续操作需要补偿上述出现的延迟 ;

        public void scheduleAtFixedRate(TimerTask task, Date firstTime,
                                        long period) {
            if (period <= 0)
                throw new IllegalArgumentException("Non-positive period.");
            sched(task, firstTime.getTime(), period);
        }
    


    schedule 方法适用场景 : 适合用于平稳执行某种任务 ; 稳定性 > 准确率 ;

    scheduleAtFixedRate 方法适用场景 : 适合用于对绝对时间敏感的任务 ; 准确率 > 稳定性 ;





    三、Timer 源码分析



    在 Timer 中定义了 TimerThread thread 成员变量 , 该成员对象在创建对象时会自动创建 ;

    TimerThread 是定义在 Timer.java 文件中的类 , 是一个自定义线程类 ; 该帮助类实现了定时器任务的执行线程 , 该线程中的定时器队列等待任务到来 , 在合适的时间执行定时器任务 ; 调度需要重复执行的任务 ; 从任务队列中 , 移出被取消的任务 , 移出不需要循环执行的任务 ;

    class TimerThread extends Thread{}
    

    在构造函数 public Timer(String name) 中 , 调用了该线程的 start() 方法 , 启动了该线程 ;

    // 省略了无关代码 
    public class Timer {
        private final TimerThread thread = new TimerThread(queue);
        public Timer(String name) {
            // 启动 TimerThread 线程 
            thread.start();
        }
    }
    

    在 TimerThread 自定义线程中的 run() 方法中 , 主要是调用了 mainLoop() 方法 ; 该方法中是一个死循环 , 从循环队列中取出 TimerTask 定时器任务 , 然后执行 ; 必须等待前一个任务执行完毕 , 才能执行下一个任务 ;






    四、Timer 部分源码注释



    // 省略了无关代码 
    public class Timer {
    
        /**
         * 定时器线程 
         * 该 TimerThread thread 对象在创建对象时会自动创建 
         */
        // Android-added: @ReachabilitySensitive
        @ReachabilitySensitive
        private final TimerThread thread = new TimerThread(queue);
    
    	// 生成序列号 , 作为定时器线程的默认名称 
        private static int serialNumber() {
            return nextSerialNumber.getAndIncrement();
        }
    
        /**
         * 创建默认定时器 , TimerThread thread 成员变量不作为守护线程
         */
        public Timer() {
            this("Timer-" + serialNumber());
        }
    
        /**
         * 创建一个定时器 , 其关联的 TimerThread thread 成员可以设置是否作为守护线程 ;
         * 如果该定时器用于用于调度重复性的维护活动 , 其守护线程会被调用 , 
         * 应用运行期间必须调用守护线程 , 
         * 但是上述操作不能影响应用的生命周期 ; 
         *
         * @param isDaemon 如果设置成 true , TimerThread thread 需要被设置成守护线程 
         */
        public Timer(boolean isDaemon) {
            this("Timer-" + serialNumber(), isDaemon);
        }
    
        /**
         * 创建一个 Timer 定时器 , 并为其其关联的线程设置指定的名称 ; 
         *
         * @param name 为 TimerThread 成员变量设置名称
         */
        public Timer(String name) {
            thread.setName(name);
            // 启动 TimerThread 线程 
            thread.start();
        }
    
        /**
         * Creates a new timer whose associated thread has the specified name,
         * and may be specified to
         * {@linkplain Thread#setDaemon run as a daemon}.
         *
         * @param name 设置关联的定时器线程名称
         * @param isDaemon 如果设置成 true 定时器线程将被设置成守护线程
         */
        public Timer(String name, boolean isDaemon) {
            thread.setName(name);
            thread.setDaemon(isDaemon);
            thread.start();
        }
    
    
    
    }
    
    /**
     * TimerThread 是定义在 Timer.java 文件中的类 ; 
     * 
     * 该帮助类实现了定时器任务的执行线程 , 该线程中的定时器队列等待任务到来 , 在合适的时间执行定时器任务 ; 
     * 调度需要重复执行的任务 ;
     * 从任务队列中 , 移出被取消的任务 , 移出不需要循环执行的任务 ; 
     */
    class TimerThread extends Thread {
        /**
         * 该标志为会 被 reaper  设置成 false , 用于通知我们定时器对象已经被销毁 ; 
         * 一旦该标志位设置成 true , 并且任务队列中没有任务时 , 没有任务去执行 , 这里就需要优雅的关闭定时器 ; 
         * 注意该字段由队列管理器维护 ; 
         */
        boolean newTasksMayBeScheduled = true;
    
        /**
         * 任务队列 , 调用 schedule 方法调度 TimerTask 定时器任务 , 就是将任务加入到该队列中 ; 
         * 定时器任务队列 , 在定时器线程中维护该队列 , 而不是在定时器中维护 , 这样避免循环引用 ; 
         * 否则定时器永远不会被回收 , 并且该线程永远不会消失 ; 
         */
        private TaskQueue queue;
    
        TimerThread(TaskQueue queue) {
            this.queue = queue;
        }
    
    	// run 方法中主要是调用 mainLoop() 方法 ; 
        public void run() {
            try {
                mainLoop();
            } finally {
                // 如果定时器被取消 , 需要杀死该线程
                synchronized(queue) {
                    newTasksMayBeScheduled = false;
                    queue.clear();  // 消除过时的引用
                }
            }
        }
    
        /**
         * 定时器轮询 
         * 里面是一个死循环 , 循环从人物队列中取出 TimerTask 定时器任务 , 然后执行 ; 
         * 必须等待前一个任务执行完毕 , 才能执行下一个任务 
         */
        private void mainLoop() {
            while (true) {
                try {
                    TimerTask task;	// 定时器任务 
                    boolean taskFired; 
                    synchronized(queue) {
                        // 如果队列为空 , 一直阻塞 , 当有任务进来时 , 解除阻塞
                        while (queue.isEmpty() && newTasksMayBeScheduled)
                            queue.wait();
                        if (queue.isEmpty())
                            break; // 如果队列为空 , 就会退出循环 , 终止该定时器 
    
                        // 队列不为空 , 查看第一个 evt , 并执行相应操作 
                        long currentTime, executionTime;
                        task = queue.getMin();
                        synchronized(task.lock) {
                            if (task.state == TimerTask.CANCELLED) {
                                queue.removeMin();
                                continue;  // 任务被取消 , 从队列中移除
                            }
                            currentTime = System.currentTimeMillis();
                            executionTime = task.nextExecutionTime;
                            if (taskFired = (executionTime<=currentTime)) {
                                if (task.period == 0) { // 该任务不需要循环执行 , 从队列中移除 
                                    queue.removeMin();
                                    task.state = TimerTask.EXECUTED;
                                } else { // 该任务需要循环执行 , 继续调度
                                    queue.rescheduleMin(
                                      task.period<0 ? currentTime   - task.period
                                                    : executionTime + task.period);
                                }
                            }
                        }
                        if (!taskFired) // 等待前一个任务执行完毕 , 如果之前的任务没有执行完毕 , 一直阻塞
                            queue.wait(executionTime - currentTime);
                    }
                    if (taskFired)  // 任务执行完毕
                        task.run();
                } catch(InterruptedException e) {
                }
            }
        }
    }
    




    五、源码及资源下载



    源码及资源下载地址 :

    展开全文
  • 定时器 Timer

    2011-08-13 18:02:14
    Java.util.Timer 与 android.widget.Chronometer 在功能上有些类似,但Timer 比Chronometer 更强大,Timer 除了可以指定循环执行的时间间隔外,还可以重复执行和不重复执行,Chronometer 类一般用来计时 Timer 定时...
     本例代码包括Handler 发送消息的简单实现过程.具有普遍意义
    Java.util.Timer 与  android.widget.Chronometer 在功能上有些类似,但Timer 比Chronometer 更强大,Timer 除了可以指定循环执行的时间间隔外,还可以重复执行和不重复执行,Chronometer 类一般用来计时

    Timer 定时执行功能是在TimeTask类中实现, Time 实际上是在线程中执行run方法,  与 Handler 相比,Handler 是将执行的动作放到Android系统的消息队列中.因此,使用Timer 执行run()时,在run()方法内不能直接更新GUI组件

    在这里介绍一下发送消息的过程:   
    首先定义一个定时类Timer,通过schedule方法启动定时器------>根据schedule 参数timerTask,创建TimerTask类,在其中run()中,定义一个Message类,初始化message.what =1, sendMessage()发送消息传到线程中
    ---------------> 在Handler 类通过handleMessage()方法,判断msg.what条件作出更新UI组件等处理,整个过程结束

    主要代码:

    public class Main extends Activity   {
     private ProgressBar progressBar;
     private Handler handler = new Handler()    {
      public void handleMessage(Message msg)    {
       switch (msg.what)     {
        // 必须在这里更新进度条组件
        case 1:
         int currentProgress = progressBar.getProgress() + 10;
         if (currentProgress > progressBar.getMax())
          currentProgress = 0;
         progressBar.setProgress(currentProgress);
         break;
       }
       super.handleMessage(msg);
      }
     };
     private TimerTask timerTask = new TimerTask()    {
      public void run()    {
       // 使用sendMesage 方法发送一条消息
       Message message = new Message();
       message.what = 1;
       handler.sendMessage(message);
      }
     };

     @Override
     public void onCreate(Bundle savedInstanceState)   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      progressBar = (ProgressBar) findViewById(R.id.progressbar);
      Timer timer = new Timer();  
      //启动Timer定时器,并在每0.2s 更新一次进度
      timer.schedule(timerTask, 0, 200);
     }
    }

    示意图



    具体代码请参见 ch08_timer工程 

    展开全文
  • 定时器Timer

    万次阅读 2020-10-20 11:11:29
    定时器Timer 为什么要使用定时器呢? 比如说一个web应用,如果这个应用规模很大,那它的日志数据是不是很多。如果一直存下来服务器的存储量怕是不行吧,需要隔一段时间删除,那么就需要一个线程每隔一段时间去...

    #定时器Timer

    为什么要使用定时器呢?

    比如说一个web应用,如果这个应用规模很大,那它的日志数据是不是很多。如果一直存下来服务器的存储量怕是不行吧,需要隔一段时间删除,那么就需要一个线程每隔一段时间去删除日志数据。

    直接来个程序:

    import java.util.Timer;
    import java.util.TimerTask;
    
    public class TestTimer {
    public static void main(String[] args) {
        Timer timer=new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("我执行了"+"  "+System.currentTimeMillis());
            }
        },1000,2000);
    }
    }
    

    运行结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ym8DcEmP-1603163485909)(https://i.imgur.com/f5551y1.png)]

    Timer 是调度器,TimerTask 调度任务。


    ##Timer类

    ###构造方法

    Timer() 创建一个新的计时器。

    Timer(boolean isDaemon) 创建一个新的定时器,其相关线程可以指定为 run as a daemon 。

    Timer(String name) 创建一个新的定时器,其相关线程具有指定的名称。

    Timer(String name, boolean isDaemon) 创建一个新的定时器,其相关线程具有指定的名称,可以指定为 run as a daemon 。

    若指定为守护进程有什么效果呢?

    //将上面程序的new Timer时候传入参数true
    Timer timer=new Timer(true);
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8Bduindu-1603163485911)(https://i.imgur.com/fVjH9hl.png)]

    因为是守护进程,main线程结束,所以不会执行直接结束。

    ###方法

    void cancel() 终止此计时器,丢弃任何当前计划的任务,将任务队列中的全部任务清空。

    import java.util.Timer;
    import java.util.TimerTask;
    
    public class TestTimer {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("我执行了" + "  " + System.currentTimeMillis());
            }
        }, 1000, 2000);
    
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        timer.cancel();
    
    }
    }
    

    结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-POqEAnyC-1603163485913)(https://i.imgur.com/zFGIz4S.png)]


    TimerTask类中的cancel()方法将自身从任务队列中清除。

    	final Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("运行了,现在" + new Date());
            }
        };
        TimerTask timerTaskTwo = new TimerTask() {
            @Override
            public void run() {
                System.out.println("Two运行了,现在" + new Date());
            }
        };
        timer.schedule(timerTask,1000,2000);
        timer.schedule(timerTaskTwo,1000,2000);
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        timerTaskTwo.cancel();
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NKFbh4ZE-1603163485914)(https://i.imgur.com/bhVJWyD.png)]


    void schedule(TimerTask task, Date time) 在指定的时间安排指定的任务执行。

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class TestTimer {
    public static void main(String[] args) throws ParseException {
        final Timer timer = new Timer();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //时间已经过去
        String dateString = "2018-06-19 21:19:00";
        String dateStringTwo = "2018-06-19 21:28:00";
        Date date = dateFormat.parse(dateString);
        Date dateTwo = dateFormat.parse(dateStringTwo);
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("运行了,现在" + new Date());
            }
        };
        TimerTask timerTaskTwo = new TimerTask() {
            @Override
            public void run() {
                System.out.println("运行了,现在" + new Date());
            }
        };
        timer.schedule(timerTask, date);
        timer.schedule(timerTaskTwo, dateTwo);
    
    }
    }
    

    结果:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tBqCpxMZ-1603163485915)(https://i.imgur.com/45AwwmH.png)]

    已经过去的时间一运行就执行任务,而未到达的时间等到达后执行任务。

    为什么程序执行完还不结束?

    这是因为系统默认当 Timer 运行结束后,如果没有手动终止,那么则只有当系统的垃圾收集被调用的时候才会对其进行回收终止。或调用 Timer 类自带的 cancel() 方法,实现 Timer 的终止。


    void schedule(TimerTask task, Date firstTime, long period)从指定的时间开始 ,对指定的任务执行重复的 固定延迟执行 。

    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.Timer;
    import java.util.TimerTask;
    
    public class TestTimer {
    public static void main(String[] args) throws ParseException {
        final Timer timer = new Timer();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //时间已经过去
        String dateString = "2018-06-19 21:44:40";
        Date date = dateFormat.parse(dateString);
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("运行了,现在" + new Date());
            }
        };
        timer.schedule(timerTask, date,2000);
    
    }
    }
    

    结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HxiRSy7C-1603163485916)(https://i.imgur.com/dkgaGhK.png)]


    void schedule(TimerTask task, long delay) 在指定的延迟之后安排指定的任务执行。

    	final Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("运行了,现在" + new Date());
            }
        };
        timer.schedule(timerTask, 2000);
    

    结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xUfxUwXe-1603163485917)(https://i.imgur.com/R4pUZ9z.png)]


    void schedule(TimerTask task, long delay, long period) 在指定的延迟之后开始 ,重新执行固定延迟执行的指定任务。

    	final Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("运行了,现在" + new Date());
            }
        };
        timer.schedule(timerTask, 3000,2000);
    

    结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ax2hsuTe-1603163485918)(https://i.imgur.com/7K7V4a6.png)]


    void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 从指定的时间开始 ,对指定的任务执行重复的固定速率执行 。

        final Timer timer = new Timer();
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        //时间已经过去
        String dateString = "2018-06-19 21:44:40";
        Date date = dateFormat.parse(dateString);
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("运行了,现在" + new Date());
            }
        };
        //timer.schedule(timerTask, date,2000);
        timer.scheduleAtFixedRate(timerTask, date,2000);
    

    结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JM5fW488-1603163485918)(https://i.imgur.com/4xecFcR.png)]

    之前未执行的任务都被执行了。

    改为

    timer.schedule(timerTask, date,2000);
    //timer.scheduleAtFixedRate(timerTask, date,2000);
    

    结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FOjdBDf4-1603163485919)(https://i.imgur.com/Vsy5G1Z.png)]
    之前未执行的任务都被没有被执行。


    void scheduleAtFixedRate(TimerTask task, long delay, long period) 在指定的延迟之后开始 ,重新执行固定速率的指定任务。

    	final Timer timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println("运行了,现在" + new Date());
            }
        };
        timer.scheduleAtFixedRate(timerTask, 3000,2000);
    

    结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VBC7FoLQ-1603163485920)(https://i.imgur.com/3kiRXfl.png)]


    ##源码探索

    若创建了无参对象 new Timer() ,查看 Timer 无参构造器:

    public Timer() {
        this("Timer-" + serialNumber());
    }
    

    这边调用 Timer(String name) 构造器,查看 serialNumber() 方法

    /**
     * This ID is used to generate thread names.
     */
    //初始化从 0 开始
    private final static AtomicInteger nextSerialNumber = new AtomicInteger(0);
    private static int serialNumber() {
    	//返回值后加 1
        return nextSerialNumber.getAndIncrement();
    }
    

    验证:

     	Timer timer = new Timer();
        Timer timerTwo = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
                System.out.println("运行了,现在" + new Date());
            }
        };
        TimerTask timerTaskTwo = new TimerTask() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName());
                System.out.println("Two运行了,现在" + new Date());
            }
        };
        timer.schedule(timerTask, 1000);
        timerTwo.schedule(timerTaskTwo, 1000);
    

    结果:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M9Wp6fHk-1603163485920)(https://i.imgur.com/ZxKsUBX.png)]

    查看 Timer(String name)

    public Timer(String name) {
        thread.setName(name);
        thread.start();
    }
    

    查看 thread

    private final TaskQueue queue = new TaskQueue();
    
    private final TimerThread thread = new TimerThread(queue);
    

    TaskQueue 任务队列用来存放任务即TimerTask,thread来跑任务。


    ###查看 TimerThread 类

    boolean newTasksMayBeScheduled = true;
    

    newTasksMayBeScheduled 用于控制当queue任务队列为空时,定时线程是否应该立刻终止(false立刻终止)

    private TaskQueue queue;
    

    queue 任务队列。

    public void run() {
        try {
    		//最核心
            mainLoop();
        } finally {
            // Someone killed this Thread, behave as if Timer cancelled
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }
    

    最核心

    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
    			// 获取任务队列的锁
                synchronized(queue) {
    				// 如果任务队列为空,并且线程没有被cancel()
                	// 则线程等待queue锁,queue.wait()方法会释放获得的queue锁
                	// 这样在Timer中sched()方法才能够获取到queue锁
                    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 { 
    							// 任务是重复执行任务,计算任务下一次应该被执行的时间,并重新排序任务队列
                                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) {
            }
        }
    }
    

    到这里你会发现为什么正常情况下Timer运行完任务后一直不结束因为一直是while(true) 除非遇到不能捕获的异常或break才会跳出。

    if (queue.isEmpty())
        break;
    

    如果是设置了newTasksMayBeScheduled状态为false跳出,也就是调用了cancel,那么queue就是空的,此时就直接跳出外部的死循环,所以cancel就是这样实现的。


    Timer类中sched方法

    下面所有的 schedule 方法都调用了sched方法

    void schedule(TimerTask task, long delay)

    void schedule(TimerTask task, Date time)

    void schedule(TimerTask task, long delay, long period)

    void schedule(TimerTask task, Date firstTime, long period)

    void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)

    void scheduleAtFixedRate(TimerTask task, long delay, long period)

    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) {
    		//判断Timer是否已经取消
            if (!thread.newTasksMayBeScheduled)
                throw new IllegalStateException("Timer already cancelled.");
    
            synchronized(task.lock) {
    			//TimerTask.VIRGIN标记任务没有被调度
                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();
        }
    }
    

    为什么要做notify操作呢?

    因为mainLoop()里面的wait()。


    Timer类中cancel方法

    public void cancel() {
        synchronized(queue) {
            thread.newTasksMayBeScheduled = false;
            queue.clear();
            queue.notify();  // In case queue was already empty.
        }
    }
    

    修改newTasksMayBeScheduled为false,删除queue队列中的任务


    ###查看TaskQueue类

    private TimerTask[] queue = new TimerTask[128];
    

    TimerTask数组,初始数组大小为128。

    int size() {
        return size;
    }
    

    size()任务队列的长度

     void add(TimerTask task) {
        // Grow backing store if necessary
        if (size + 1 == queue.length)
    		//扩2倍长度
            queue = Arrays.copyOf(queue, 2*queue.length);
    
        queue[++size] = task;
        fixUp(size);
    }
    

    add(TimerTaskt)为增加一个任务

    TimerTask getMin() {
        return queue[1];
    }
    

    getMin()获取当前排序后最近需要执行的一个任务,下标为1,队列头部0是不做任何操作的

     TimerTask get(int i) {
        return queue[i];
    }
    

    get(int i)获取指定下标的数据,当然包括下标 0

    void removeMin() {
        queue[1] = queue[size];
        queue[size--] = null;  // Drop extra reference to prevent memory leak
        fixDown(1);
    }
    

    removeMin()为删除当前最近执行的任务,也就是第一个元素,通常只调度一次的任务,在执行完后,调用此方法,就可以将TimerTask从队列中移除。

    void quickRemove(int i) {
        assert i <= size;
    
        queue[i] = queue[size];
        queue[size--] = null;  // Drop extra ref to prevent memory leak
    }
    

    quickRmove(int i) 删除指定的元素,一般来说是不会调用这个方法的,这个方法只有在 Timer 发生 purge 的时候,并且当对应的 TimerTask调用了 cancel 方法的时候,才会被调用这个方法,也就是取消某个TimerTask,然后就会从队列中移除(注意如果任务在执行中是,还是仍然在执行中的,虽然在队列中被移除了),还有就是这个 cancel 方法并不是 Timer 的 cancel 方法而是 TimerTask,一个是调度器的,一个是单个任务的,最后注意,这个 quickRmove 完成后,是将队列最后一个元素补充到这个位置,所以此时会造成顺序不一致的问题,后面会有方法进行回补。

    void rescheduleMin(long newTime) {
        queue[1].nextExecutionTime = newTime;
        fixDown(1);
    }
    

    rescheduleMin(long newTime)是重新设置当前执行的任务的下一次执行时间,并在队列中将其从新排序到合适的位置,而调用的是后面说的fixDown方法。

    boolean isEmpty() {
        return size==0;
    }
    

    isEmpty() 判空。

    void clear() {
        // Null out task references to prevent memory leak
        for (int i=1; i<=size; i++)
            queue[i] = null;
    
        size = 0;
    }
    

    clear() 清理。

    对于 fixUp(int k) 和 fixDown(int k) 方法来讲,前者是当新增一个task的时候,首先将元素放在队列的尾部,然后向前找是否有比自己还要晚执行的任务,如果有,就将两个任务的顺序进行交换一下。而fixDown正好相反,执行完第一个任务后,需要加上一个时间片得到下一次执行时间,从而需要将其顺序与后面的任务进行对比下。

    heapify(),其实就是将队列的后半截,全部做一次fixeDown的操作,这个操作主要是为了回补quickRemove方法,当大量的quickRmove后,顺序被打乱后,此时将一半的区域做一次非常简单的排序即可。


    ###TimerTask抽象类

    //对象锁
    final Object lock = new Object();
    
    //task状态初始VIRGIN
    int state = VIRGIN;
    
    //这项任务尚未安排好
    static final int VIRGIN = 0;
    
    //这个任务计划执行。如果它是一个非重复的任务,那么它还没有被执行
    static final int SCHEDULED   = 1;
    
    //此非重复任务已经执行(或目前正在执行)并没有被取消。
    static final int EXECUTED    = 2;
    
    //此任务被取消了
    static final int CANCELLED   = 3;
    
    //任务下次执行时间
    long nextExecutionTime;
    
    //重复任务的周期(毫秒)。一个积极的值表示固定利率执行。负值表示固定延迟执行。值为0表示非重复任务。
    long period = 0;
    
    //取消任务
    public boolean cancel()
    
    //计划执行时间
    public long scheduledExecutionTime() {
        synchronized(lock) {
            return (period < 0 ? nextExecutionTime + period
                               : nextExecutionTime - period);
        }
    }
    

    #总结

    参考
    Java多线程编程核心技术
    https://blog.csdn.net/xieyuooo/article/details/8607220
    https://www.jianshu.com/p/58a5b0853451

    Timer类其实内部有个thread,还有一个queue(来存储task),task会按一定的方式将任务排队处理,thread中的run方法无限循环task队列,执行task中的run()。

    才学疏浅,有什么问题请大家指出来。十分感谢!

    展开全文
  • JMeter(八):定时器Timer经典介绍

    万次阅读 2017-10-23 19:55:29
    性能测试方向:后起之秀JMeter与革命前辈Loadrunner的比较,JMeter测试工具的Timer可以根据实际场景设置思考时间,用于等待或是集合点同时并发操作;言归正传,我们来看看JMeter的Timer成员有哪些,及具体作用? 1...
  • Timer定时器

    千次阅读 2016-01-06 19:33:31
    JAVA定时器Timer
  • timer定时器

    2016-08-22 10:13:28
    定时器方法(1) // 添加定时器方法 - (void)addTimer {#warning mark - 如果在用定时器是忽略它的运行模式时就用下面这个方法 ... // self.timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self sele
  • 定时器timer

    2016-07-21 17:14:13
    1.定义一个timer定时器中断函数fun struct timer_list timer; void fun(); 2.先初始化timer init_timer(&timer); then 对timer的相关参数赋值: timer.function = fun; timer.expires = jiffies + ...
  • 定时器TIMER

    2013-07-11 10:26:38
    Timer事件,即定时器事件,是在游戏编程中,经常使用的一个事件。借助它可以产生定时执行动作的效果。这篇文章,就和大家一起探讨一下如何使用SetTimer()函数。 1、SetTimer定义在那里? SetTimer表示的是...
  • timer 定时器

    2011-06-08 15:54:00
    定时器: Kal Timer: KAL timer 是非常底层的timer, 它的实现是直接封装MTK的RTOS(nuleus)的timer,实现方式是由HISR,从而这种timer具有很高的优先级,也就是说,当这个timer 超时时,就会触发一个HISR(高级中断 ...
  • Timer定时器用法详解

    万次阅读 2019-07-06 17:30:20
    先看API和结论: /** timer总结: Timer timer = new Timer(); //其中会调用this(..., 即它以Timer+序列号为该定时器的名字 Timer timer = new Timer(String name); //以name作为该定时器的名字 Timer time...
  • C# Timer 定时器应用

    千次阅读 2014-07-24 18:33:28
    基于服务器的计时器(System.Timers.Timer)  System.Timers.Timer不依赖窗体,是从线程池唤醒线程,是传统的计时器为了在服务器环境上运行而优化后的更新版本  在VS2005的工具箱中没有提供现成的控件,需要手工...
  • QML:Timer定时器使用

    千次阅读 2019-11-13 10:06:39
    效果图 关键代码 import QtQuick 2.9 import QtQuick.Window 2.2 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.2 Window { visible: true width: 640 ... title: qsTr("定...
  • LayaAir 定时器 Timer

    千次阅读 2019-01-07 15:48:16
    结束定时器 优化策略 Timer  Package laya.utils 类 public class Timer Inheritance Timer Object Timer 是时钟管理类,它是一个单例,不要手动实例化此类,应该通过 Laya....
  • Golang timer定时器

    千次阅读 2018-11-05 11:12:45
    Golang timer定时器   timer 计时器 用于在指定的Duration类型时间后调用函数或计算表达式。 如果只是想指定时间之后执行,使用time.Sleep() 使用NewTimer(),可以返回的Timer类型在计时器到期之前,取消该计时器...
  • 定时器Timer的使用

    2014-04-18 10:23:42
    TimerTask timerTask = new TimerTask(){ @Override public void run() { System.out.println("执行一次。... Timer timer = new Timer(); timer.scheduleAtFixedRate(timerTask, 1000
  • java定时器timer制作

    2018-08-17 11:23:26
    在开放过程当中,遇到项目需要用到定时器,然后查找了一些资料并进行了整理。希望对正在学习同学有所帮助。
  • Timer定时器详解

    千次阅读 2016-10-15 15:00:17
    Timer定时器详解
  • Python Timer定时器

    2019-08-23 21:04:17
    Timer是在threading模块下的、Thread类的派生类,它用于在指定时间后调用一个方法。 Timer的构造方法: Timer(interval,func,args,kwargs) ...实例化Timer,即创建了一个定时器Timer指向的方法会在指定...
  • Android 定时器Timer

    2019-07-29 11:00:29
    定义一个定时器,处理handler Timer timer = new Timer(); TimerTask task = new TimerTask() { @Override public void run() { Thread thread = new Thread(new Runnab...

空空如也

1 2 3 4 5 ... 20
收藏数 49,544
精华内容 19,817
关键字:

timer