zoukankan      html  css  js  c++  java
  • Java 多线程之Timer与ScheduledExecutorService

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

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

    Timer的源码:

    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的子类,可以看出内部只有一个线程。下面看个例子:

     
    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后执行,但是看运行结果:

     
    1. task1 invoked ! 1000  
    2. task2 invoked ! 4000  

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

     
    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. }  


    输出结果:

     
    1. task1 invoked ! 1001  
    2. task2 invoked ! 3001  

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

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

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

     
    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周期性的执行某个操作,输出结果:

      task2 invoked!  

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


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

     
    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的运行:

     
    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。

  • 相关阅读:
    js-AOP
    jQueryUI之autocomplete
    nginx安装配置
    oracle结构语法
    ajax/表单提交 多个相同name的处理方法
    ES6模块化
    docker运维
    帆软报表
    oracle锁表
    香港到大陆IPLC节点故障
  • 原文地址:https://www.cnblogs.com/rickzhai/p/7936453.html
Copyright © 2011-2022 走看看