• Java 并发专题 : Timer的缺陷 用ScheduledExecutorService替代


    继续并发,上篇博客对于ScheduledThreadPoolExecutor没有进行介绍,说过会和Timer一直单独写一篇Blog.

    1、Timer管理延时任务的缺陷

    a、以前在项目中也经常使用定时器,比如每隔一段时间清理项目中的一些垃圾文件,每个一段时间进行数据清洗;然而Timer是存在一些缺陷的,因为Timer在执行定时任务时只会创建一个线程,所以如果存在多个任务,且任务时间过长,超过了两个任务的间隔时间,会发生一些缺陷:下面看例子:

    Timer的源码:

    [java] view plaincopy
     
    1. public class Timer {  
    2.     /** 
    3.      * The timer task queue.  This data structure is shared with the timer 
    4.      * thread.  The timer produces tasks, via its various schedule calls, 
    5.      * and the timer thread consumes, executing timer tasks as appropriate, 
    6.      * and removing them from the queue when they're obsolete. 
    7.      */  
    8.     private TaskQueue queue = new TaskQueue();  
    9.   
    10.     /** 
    11.      * The timer thread. 
    12.      */  
    13.     private TimerThread thread = new TimerThread(queue);  


    TimerThread是Thread的子类,可以看出内部只有一个线程。下面看个例子:

    [java] view plaincopy
     
    1. package com.zhy.concurrency.timer;  
    2.   
    3. import java.util.Timer;  
    4. import java.util.TimerTask;  
    5.   
    6. public class TimerTest  
    7. {  
    8.     private static long start;  
    9.   
    10.     public static void main(String[] args) throws Exception  
    11.     {  
    12.   
    13.         TimerTask task1 = new TimerTask()  
    14.         {  
    15.             @Override  
    16.             public void run()  
    17.             {  
    18.   
    19.                 System.out.println("task1 invoked ! "  
    20.                         + (System.currentTimeMillis() - start));  
    21.                 try  
    22.                 {  
    23.                     Thread.sleep(3000);  
    24.                 } catch (InterruptedException e)  
    25.                 {  
    26.                     e.printStackTrace();  
    27.                 }  
    28.   
    29.             }  
    30.         };  
    31.         TimerTask task2 = new TimerTask()  
    32.         {  
    33.             @Override  
    34.             public void run()  
    35.             {  
    36.                 System.out.println("task2 invoked ! "  
    37.                         + (System.currentTimeMillis() - start));  
    38.             }  
    39.         };  
    40.         Timer timer = new Timer();  
    41.         start = System.currentTimeMillis();  
    42.         timer.schedule(task1, 1000);  
    43.         timer.schedule(task2, 3000);  
    44.   
    45.     }  
    46. }  


    定义了两个任务,预计是第一个任务1s后执行,第二个任务3s后执行,但是看运行结果:

    [java] view plaincopy
     
    1. task1 invoked ! 1000  
    2. task2 invoked ! 4000  

    task2实际上是4后才执行,正因为Timer内部是一个线程,而任务1所需的时间超过了两个任务间的间隔导致。下面使用ScheduledThreadPool解决这个问题:

    [java] view plaincopy
     
    1. package com.zhy.concurrency.timer;  
    2.   
    3. import java.util.TimerTask;  
    4. import java.util.concurrent.Executors;  
    5. import java.util.concurrent.ScheduledExecutorService;  
    6. import java.util.concurrent.TimeUnit;  
    7.   
    8. public class ScheduledThreadPoolExecutorTest  
    9. {  
    10.     private static long start;  
    11.   
    12.     public static void main(String[] args)  
    13.     {  
    14.         /** 
    15.          * 使用工厂方法初始化一个ScheduledThreadPool 
    16.          */  
    17.         ScheduledExecutorService newScheduledThreadPool = Executors  
    18.                 .newScheduledThreadPool(2);  
    19.           
    20.         TimerTask task1 = new TimerTask()  
    21.         {  
    22.             @Override  
    23.             public void run()  
    24.             {  
    25.                 try  
    26.                 {  
    27.   
    28.                     System.out.println("task1 invoked ! "  
    29.                             + (System.currentTimeMillis() - start));  
    30.                     Thread.sleep(3000);  
    31.                 } catch (Exception e)  
    32.                 {  
    33.                     e.printStackTrace();  
    34.                 }  
    35.   
    36.             }  
    37.         };  
    38.   
    39.         TimerTask task2 = new TimerTask()  
    40.         {  
    41.             @Override  
    42.             public void run()  
    43.             {  
    44.                 System.out.println("task2 invoked ! "  
    45.                         + (System.currentTimeMillis() - start));  
    46.             }  
    47.         };  
    48.         start = System.currentTimeMillis();  
    49.         newScheduledThreadPool.schedule(task1, 1000, TimeUnit.MILLISECONDS);  
    50.         newScheduledThreadPool.schedule(task2, 3000, TimeUnit.MILLISECONDS);  
    51.     }  
    52. }  


    输出结果:

    [java] view plaincopy
     
    1. task1 invoked ! 1001  
    2. task2 invoked ! 3001  

    符合我们的预期结果。因为ScheduledThreadPool内部是个线程池,所以可以支持多个任务并发执行。

    2、Timer当任务抛出异常时的缺陷

    如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行:

    [java] view plaincopy
     
    1. package com.zhy.concurrency.timer;  
    2.   
    3. import java.util.Date;  
    4. import java.util.Timer;  
    5. import java.util.TimerTask;  
    6.   
    7.   
    8. public class ScheduledThreadPoolDemo01  
    9. {  
    10.   
    11.   
    12.     public static void main(String[] args) throws InterruptedException  
    13.     {  
    14.   
    15.         final TimerTask task1 = new TimerTask()  
    16.         {  
    17.   
    18.             @Override  
    19.             public void run()  
    20.             {  
    21.                 throw new RuntimeException();  
    22.             }  
    23.         };  
    24.   
    25.         final TimerTask task2 = new TimerTask()  
    26.         {  
    27.   
    28.             @Override  
    29.             public void run()  
    30.             {  
    31.                 System.out.println("task2 invoked!");  
    32.             }  
    33.         };  
    34.           
    35.         Timer timer = new Timer();  
    36.         timer.schedule(task1, 100);  
    37.         timer.scheduleAtFixedRate(task2, new Date(), 1000);  
    38.           
    39.           
    40.   
    41.     }  
    42. }  


    上面有两个任务,任务1抛出一个运行时的异常,任务2周期性的执行某个操作,输出结果:

    [java] view plaincopy
     
    1. task2 invoked!  
    2. Exception in thread "Timer-0" java.lang.RuntimeException  
    3.     at com.zhy.concurrency.timer.ScheduledThreadPoolDemo01$1.run(ScheduledThreadPoolDemo01.java:24)  
    4.     at java.util.TimerThread.mainLoop(Timer.java:512)  
    5.     at java.util.TimerThread.run(Timer.java:462)  


    由于任务1的一次,任务2也停止运行了。。。下面使用ScheduledExecutorService解决这个问题:

    [java] view plaincopy
     
    1. package com.zhy.concurrency.timer;  
    2.   
    3. import java.util.Date;  
    4. import java.util.Timer;  
    5. import java.util.TimerTask;  
    6. import java.util.concurrent.Executors;  
    7. import java.util.concurrent.ScheduledExecutorService;  
    8. import java.util.concurrent.TimeUnit;  
    9.   
    10.   
    11. public class ScheduledThreadPoolDemo01  
    12. {  
    13.   
    14.   
    15.     public static void main(String[] args) throws InterruptedException  
    16.     {  
    17.   
    18.         final TimerTask task1 = new TimerTask()  
    19.         {  
    20.   
    21.             @Override  
    22.             public void run()  
    23.             {  
    24.                 throw new RuntimeException();  
    25.             }  
    26.         };  
    27.   
    28.         final TimerTask task2 = new TimerTask()  
    29.         {  
    30.   
    31.             @Override  
    32.             public void run()  
    33.             {  
    34.                 System.out.println("task2 invoked!");  
    35.             }  
    36.         };  
    37.           
    38.           
    39.           
    40.         ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);  
    41.         pool.schedule(task1, 100, TimeUnit.MILLISECONDS);  
    42.         pool.scheduleAtFixedRate(task2, 0 , 1000, TimeUnit.MILLISECONDS);  
    43.   
    44.     }  
    45. }  


    代码基本一致,但是ScheduledExecutorService可以保证,task1出现异常时,不影响task2的运行:

    [java] view plaincopy
     
    1. task2 invoked!  
    2. task2 invoked!  
    3. task2 invoked!  
    4. task2 invoked!  
    5. task2 invoked!<span style="font-family: Arial, Helvetica, sans-serif;">...</span>  


    3、Timer执行周期任务时依赖系统时间

    Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化,ScheduledExecutorService基于时间的延迟,不会由于系统时间的改变发生执行变化。

    上述,基本说明了在以后的开发中尽可能使用ScheduledExecutorService(JDK1.5以后)替代Timer。

    好了,如果博客中存在错误,请留言指出~

  • 相关阅读:
    springmvc log4j 配置
    intellij idea maven springmvc 环境搭建
    spring,property not found on type
    intellij idea maven 工程生成可执行的jar
    device eth0 does not seem to be present, delaying initialization
    macos ssh host配置及免密登陆
    centos7 搭建 docker 环境
    通过rest接口获取自增id (twitter snowflake算法)
    微信小程序开发体验
    gitbook 制作 beego 参考手册
  • 原文地址:https://www.cnblogs.com/dongweiq/p/3934320.html
Copyright © 2020-2023  润新知