zoukankan      html  css  js  c++  java
  • java定时任务

    一、简介

           在java编程中,有时需要在指定时间或者指定频率来执行任务,可以使用Timer和TimerTask定时器。

    Timer类

    在工具类Timer中,提供了四个构造方法,每个构造方法都启动了计时器线程,同时Timer类可以保证多个线程可以共享单个Timer对象而无需进行外部同步,所以Timer类是线程安全的。但是由于每一个Timer对象对应的是单个后台线程,用于顺序执行所有的计时器任务,一般情况下我们的线程任务执行所消耗的时间应该非常短,但是由于特殊情况导致某个定时器任务执行的时间太长,那么他就会“独占”计时器的任务执行线程,其后的所有线程都必须等待它执行完,这就会延迟后续任务的执行,使这些任务堆积在一起,具体情况我们后面分析。

    当程序初始化完成Timer后,定时任务就会按照我们设定的时间去执行,Timer提供了schedule方法,该方法有多中重载方式来适应不同的情况,如下:

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

    schedule(TimerTask task, Date firstTime, long period) :安排指定的任务在指定的时间开始进行重复的固定延迟执行。

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

    schedule(TimerTask task, long delay, long period) :安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。

    同时也重载了scheduleAtFixedRate方法,scheduleAtFixedRate方法与schedule相同,只不过他们的侧重点不同,区别后面分析。

    scheduleAtFixedRate(TimerTask task, Date firstTime, long period):安排指定的任务在指定的时间开始进行重复的固定速率执行。

    scheduleAtFixedRate(TimerTask task, long delay, long period):安排指定的任务在指定的延迟后开始进行重复的固定速率执行。

    TimerTask

    TimerTask类是一个抽象类,由Timer 安排为一次执行或重复执行的任务。它有一个抽象方法run()方法,该方法用于执行相应计时器任务要执行的操作。因此每一个具体的任务类都必须继承TimerTask,然后重写run()方法。

    另外它还有两个非抽象的方法:

    boolean cancel():取消此计时器任务。

    long scheduledExecutionTime():返回此任务最近实际执行的安排执行时间。

    二、实例

    2.1、指定延迟时间执行定时任务

     1 public class TimerTest01 {
     2     Timer timer;
     3     public TimerTest01(int time){
     4         timer = new Timer();
     5         timer.schedule(new TimerTaskTest01(), time * 1000);
     6     }
     7     
     8     public static void main(String[] args) {
     9         System.out.println("timer begin....");
    10         new TimerTest01(3);
    11     }
    12 }
    13 
    14 public class TimerTaskTest01 extends TimerTask{
    15 
    16     public void run() {
    17         System.out.println("Time's up!!!!");
    18     }
    19 }

    2.2、在指定时间执行定时任务

    public class TimerTest02 {
        Timer timer;
        
        public TimerTest02(){
            Date time = getTime();
            System.out.println("指定时间time=" + time);
            timer = new Timer();
            timer.schedule(new TimerTaskTest02(), time);
        }
        
        public Date getTime(){
            Calendar calendar = Calendar.getInstance();
            calendar.set(Calendar.HOUR_OF_DAY, 11);
            calendar.set(Calendar.MINUTE, 39);
            calendar.set(Calendar.SECOND, 00);
            Date time = calendar.getTime();
            
            return time;
        }
        
        public static void main(String[] args) {
            new TimerTest02();
        }
    }
    
    public class TimerTaskTest02 extends TimerTask{
    
        @Override
        public void run() {
            System.out.println("指定时间执行线程任务...");
        }
    }
    当时间到达11:39:00时就会执行该线程任务,当然大于该时间也会执行!!

    2.3、在延迟指定时间后以指定的间隔时间循环执行定时任务

    public class TimerTest03 {
        Timer timer;
        
        public TimerTest03(){
            timer = new Timer();
            timer.schedule(new TimerTaskTest03(), 1000, 2000);
        }
        
        public static void main(String[] args) {
            new TimerTest03();
        }
    }
    
    public class TimerTaskTest03 extends TimerTask{
    
        @Override
        public void run() {
            Date date = new Date(this.scheduledExecutionTime());
            System.out.println("本次执行该线程的时间为:" + date);
        }
    }

    三、Timer的缺陷

    3.1、Timer的缺陷

    Timer计时器可以定时(指定时间执行任务)、延迟(延迟5秒执行任务)、周期性地执行任务(每隔个1秒执行任务),但是,Timer存在一些缺陷。首先Timer对调度的支持是基于绝对时间的,而不是相对时间,所以它对系统时间的改变非常敏感。其次Timer线程是不会捕获异常的,如果TimerTask抛出的了未检查异常则会导致Timer线程终止,同时Timer也不会重新恢复线程的执行,他会错误的认为整个Timer线程都会取消。同时,已经被安排单尚未执行的TimerTask也不会再执行了,新的任务也不能被调度。故如果TimerTask抛出未检查的异常,Timer将会产生无法预料的行为。

    1、Timer管理时间延迟缺陷

    前面Timer在执行定时任务时只会创建一个线程任务,如果存在多个线程,若其中某个线程因为某种原因而导致线程任务执行时间过长,超过了两个任务的间隔时间,会发生一些缺陷:

    public class TimerTest04 {
        private Timer timer;
        public long start;   
        
        public TimerTest04(){
            this.timer = new Timer();
            start = System.currentTimeMillis();
        }
        
        public void timerOne(){
            timer.schedule(new TimerTask() {
                public void run() {
                    System.out.println("timerOne invoked ,the time:" + (System.currentTimeMillis() - start));
                    try {
                        Thread.sleep(4000);    //线程休眠3000
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }, 1000);
        }
        
        public void timerTwo(){
            timer.schedule(new TimerTask() {
                public void run() {
                    System.out.println("timerOne invoked ,the time:" + (System.currentTimeMillis() - start));
                }
            }, 3000);
        }
        
        public static void main(String[] args) throws Exception {
            TimerTest04 test = new TimerTest04();
            
            test.timerOne();
            test.timerTwo();
        }
    }

    按照我们正常思路,timerTwo应该是在3s后执行,其结果应该是:

    timerOne invoked ,the time:1001
    timerOne invoked ,the time:3001

    但是事与愿违,timerOne由于sleep(4000),休眠了4S,同时Timer内部是一个线程,导致timeOne所需的时间超过了间隔时间,结果:

    timerOne invoked ,the time:1000
    timerOne invoked ,the time:5000

    对于Timer的缺陷,我们可以考虑 ScheduledThreadPoolExecutor 来替代。Timer是基于绝对时间的,对系统时间比较敏感,而ScheduledThreadPoolExecutor 则是基于相对时间;Timer是内部是单一线程,而ScheduledThreadPoolExecutor内部是个线程池,所以可以支持多个任务并发执行。

    3.2、用ScheduledExecutorService替代Timer

    public class ScheduledExecutorTest {
        private  ScheduledExecutorService scheduExec;
        
        public long start;
        
        ScheduledExecutorTest(){
            this.scheduExec =  Executors.newScheduledThreadPool(2);  
            this.start = System.currentTimeMillis();
        }
        
        public void timerOne(){
            scheduExec.schedule(new Runnable() {
                public void run() {
                    System.out.println("timerOne,the time:" + (System.currentTimeMillis() - start));
                    try {
                        Thread.sleep(4000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            },1000,TimeUnit.MILLISECONDS);
        }
        
        public void timerTwo(){
            scheduExec.schedule(new Runnable() {
                public void run() {
                    System.out.println("timerTwo,the time:" + (System.currentTimeMillis() - start));
                }
            },2000,TimeUnit.MILLISECONDS);
        }
        
        public static void main(String[] args) {
            ScheduledExecutorTest test = new ScheduledExecutorTest();
            test.timerOne();
            test.timerTwo();
        }
    }
  • 相关阅读:
    【POJ 1958】 Strange Towers of Hanoi
    【HNOI 2003】 激光炸弹
    【POJ 3263】 Tallest Cow
    【POJ 2689】 Prime Distance
    【POJ 2777】 Count Color
    【POJ 1995】 Raising Modulo Numbers
    【POJ 1845】 Sumdiv
    6月16日省中集训题解
    【TJOI 2018】数学计算
    【POJ 1275】 Cashier Employment
  • 原文地址:https://www.cnblogs.com/ilinuxer/p/4830210.html
Copyright © 2011-2022 走看看