zoukankan      html  css  js  c++  java
  • Java定时器Timer的使用

      定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程的方式进行处理,所以它和多线程技术还是有非常大的关联的。

      1、看下面代码:

     1     public void TestUserNvrConnectOnline() {
     2         Timer timer = new Timer(true);
     3         timer.schedule(new TimerTask() {
     4             public void run() {
     5                 //你要定时执行的功能
     6                 isUserNvrConnectLast();
     7             }
     8         }, 0, 60 * 1000);
     9 
    10     }
    View Code

      周期执行任务,Timer.schedule(TimerTask task,long firstTime,long period),参数task是要执行代码的主体,参数firstTime是Timer在系统启动后第一次运行的时间毫秒值,参数period是代码循环运行的时间间隔,每次最少等待period执行一次。

      2、Timer还有其他几种使用方式,基本上大同小异,比如:

        
        private Set<Integer> unitTimeSet = new HashSet<>(24);//每天更新整点数,把已经检查的时间记录下来
     1     public void checkDeviceDeopped() {
     2         Calendar calendar = Calendar.getInstance();
     3         //此时要在 第一次执行定时任务的时间加一小时,以便此任务在下个时间点执行。
     4         calendar.add(Calendar.HOUR_OF_DAY,1);
     5         calendar.set(Calendar.MINUTE, 0);
     6         calendar.set(Calendar.SECOND, 0);
     7         java.util.Date date = calendar.getTime(); //第一次执行定时任务的时间
     8 
     9         Timer timer = new Timer(true);
    10         timer.schedule(new TimerTask() {
    11             public void run() {
    12                 Calendar cal = Calendar.getInstance();
    13                 int hour = cal.get(Calendar.HOUR_OF_DAY);
    14 
    15                 if (hour == 0){
    16                     unitTimeSet.clear();
    17                 }
    18                 if (hour % unitTime == 0 && !unitTimeSet.contains(hour)){
    19                     Session session = sessionFactory.openSession();
    20                     cal.set(Calendar.MINUTE, 0);
    21                     cal.set(Calendar.SECOND, 0);
    22                     java.util.Date endTime = cal.getTime();
    23                     cal.add(Calendar.HOUR_OF_DAY,-1);
    24                     java.util.Date startTime = cal.getTime();
    25                     for (String deviceId : mapNvr.keySet()) {
    26                         //查询设备在指定时间内的掉线次数
    27                         Integer offLineNum = 0;
    28                         try {
    29                             offLineNum = (Integer) session.createQuery("SELECT COUNT(*) FROM SystemLog WHERE workerid= :deviceId AND dt<= :endTime AND dt>= :startTime ").setParameter("deviceId", deviceId)
    30                                     .setParameter("endTime", endTime, TemporalType.TIMESTAMP).setParameter("startTime", startTime, TemporalType.TIMESTAMP).uniqueResult();
    31                         }catch (Exception e){
    32                             e.printStackTrace();
    33                         }
    34 
    35                         if (offLineNum >= 10){
    36                             //发送邮件
    37                             try {
    38                                 CompletableFuture.runAsync(() -> {
    39                                     sendMail("NUM", deviceId);
    40                                 });
    41                             }  catch (Exception e) {
    42                                 e.printStackTrace();
    43                             }
    44                         }
    45                     }
    46                 }
    47                 unitTimeSet.add(hour);
    48             }
    49         }, date,60 * 60 * 1000); 
    50 }

      上面的代码是在每天的整点执行一次定时任务,查看设备离线次数,如果超过10次就发送邮件,循环时长和离线次数可另外定义变量进行控制,发送邮件调用了异步,感兴趣的朋友可以查看我的另外文章Java异步CompletableFuture的使用

      3、另外形式的定时器不再上代码,大体如下:

        1)、public void schedule(TimerTask task, long delay)

          经过时长delay后执行,仅执行一次。

        2)、public void schedule(TimerTask task, Date time)

          在指定的时间点time执行,仅执行一次。

        3)、public void scheduleAtFixedRate(TimerTask task, long delay, long period)

          调用一个task,第一次在delay时长后执行,以后每次经过period执行一次,看起来和schedule这种方式一样,实际是有区别的。schedule在计算下一次执行的时间的时候,是通过当前执行的时间(在任务执行前得到) + 时间片period,而scheduleAtFixedRate方法是通过当前需要执行的时间(计算出现在应该执行的时间)+ 时间片,前者是程序运行的实际时间,是动态的,而后者是指定的理论时间点,代码编译后就是固定不变的。我们可以看一下源码部分,schedule(TimerTask task, long delay,long 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);
        }

    再看scheduleAtFixedRate(TimerTask task, long delay, long period)的源码如下:

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

    看起来没有区别,唯一的区别就是sched(task, System.currentTimeMillis()+delay, period)的第三个参数,schedule加了一个负号,其实就是在调用sched方法时用来区分schedule和scheduleAtFixedRate的。我们看sched的代码如下:

     1     private void sched(TimerTask task, long time, long period) {
     2         if (time < 0)
     3             throw new IllegalArgumentException("Illegal execution time.");
     4 
     5         synchronized(queue) {
     6             if (!thread.newTasksMayBeScheduled)
     7                 throw new IllegalStateException("Timer already cancelled.");
     8 
     9             synchronized(task.lock) {
    10                 if (task.state != TimerTask.VIRGIN)
    11                     throw new IllegalStateException(
    12                         "Task already scheduled or cancelled");
    13                 task.nextExecutionTime = time;
    14                 task.period = period;
    15                 task.state = TimerTask.SCHEDULED;
    16             }
    17 
    18             queue.add(task);
    19             if (queue.getMin() == task)
    20                 queue.notify();
    21         }
    22     }

    queue是一个队列,他在做这个操作的时候,发生了同步,所以在timer级别,这个是线程安全的,最后将task相关的参数赋值,主要包含nextExecutionTime(下一次执行时间),period(时间片),state(状态),然后将它放入queue队列中,做一次notify操作;我们看一下queue的结构:TaskQueue

    1     class TaskQueue {
    2 
    3     private TimerTask[] queue = new TimerTask[128];
    4 
    5     private int size = 0;

    可以看到,TaskQueue的结构是一个数组,加一个size,有点像ArrayList,ArrayList可以扩容,TaskQueue也可以,只是会造成内存拷贝,所以对于一个Timer来讲,只要内部的task个数不超过128是不会扩容的;内部 提供了add(TimerTask)、size()、getMin()、get(int)、removeMin()、quickRemove(int)、 rescheduleMin(long newTime)、isEmpty()、clear()、fixUp()、fixDown()、heapify()等,这里不再详述,有兴趣的朋友可以查阅相关资料。

     

        4)、public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period)

          和3)的执行时间一样

      需要注意,TimerTask 是以队列的方式一个一个被顺序运行的,所以执行的时间和预期的时间可能不一致,因为前面的任务可能消耗的时间较长,则后面的任务运行的时间会被延迟。延迟的任务具体开始的时间,就是依据前面任务的"结束时间"。Timer仍有很多需要注意的地方,也有一些其他的用法,不再赘述,有兴趣的伙伴可以继续查阅。

      参考来源:https://www.cnblogs.com/0201zcr/p/4703061.html

  • 相关阅读:
    C#基础知识(以宝马,车,车轮为例)
    JAVA之本地图片复制
    JAVA获取PC每个盘符,获取每个盘符总大小,剩余空间大小
    Extjs window autoload
    jquery入门(一)
    extjs 鼠标滑过grid时产生提示grid列中的值
    poi导出
    Extjs 报表同值合并方法
    强大的grep命令
    job
  • 原文地址:https://www.cnblogs.com/JohanChan/p/11250782.html
Copyright © 2011-2022 走看看