Timer的缺陷 用ScheduledExecutorService替代

GIT: https://github.com/whtchl/JavaConcurrentTemplate

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

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

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

Timer的源码:

[java]  view plain  copy
  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 plain  copy
  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 plain  copy
  1. task1 invoked ! 1000  
  2. task2 invoked ! 4000  
task2实际上是4后才执行,正因为Timer内部是一个线程,而任务1所需的时间超过了两个任务间的间隔导致。下面使用ScheduledThreadPool解决这个问题:

[java]  view plain  copy
  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 plain  copy
  1. task1 invoked ! 1001  
  2. task2 invoked ! 3001  
符合我们的预期结果。因为ScheduledThreadPool内部是个线程池,所以可以支持多个任务并发执行。

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

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

[java]  view plain  copy
  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 plain  copy
  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 plain  copy
  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 plain  copy
  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。

<think>好的,我现在需要帮助用户了解如何在Java中实现计时器功能,特别是使用Timer类和ScheduledExecutorService。首先,我得回忆一下这两个类的相关知识,并参考用户提供的引用内容,确保信息准确。 用户提供的引用提到,Timer类在遇到未检查异常时会终止整个线程,导致任务无法继续执行。而ScheduledExecutorService是基于线程池的,能更好地处理异常和任务调度。所以,在回答时需要强调两者的区别,尤其是异常处理方面。 接下来,我需要按照用户的要求,给出两种实现方式的示例代码。首先,对于Timer类,虽然它简单易用,但存在缺陷。需要编写一个继承TimerTask的类,并在run方法中定义任务。然后在主程序中使用Timer.schedule()来调度任务。同时,要提醒用户注意Timer的缺点,如异常处理的问题,引用中的[^1]和[^2]可以作为支持。 然后是ScheduledExecutorService的示例。这里需要使用Executors.newScheduledThreadPool创建线程池,然后使用scheduleAtFixedRate或scheduleWithFixedDelay来安排任务。需要解释参数的意义,比如初始延迟、周期等。同时,要指出它的优势,比如线程池管理和更好的异常处理,引用[^3]中的示例可以作为参考。 用户还希望生成相关问题,我需要考虑用户可能进一步想知道的内容,比如两种方式的区别、异常处理的最佳实践、如何动态调整任务周期,以及替代方案如Quartz框架。这些问题可以帮助用户深入理解并解决实际开发中的问题。 最后,要确保回答结构清晰,符合用户的要求,使用正确的LaTeX格式,并添加引用标识。同时,代码部分需要用反引号包裹,保持格式正确。检查是否有遗漏的要点,比如Timer缺陷ScheduledExecutorService的优点是否都涵盖,以及引用是否正确对应。</think>### Java计时器实现方法 在Java中,可以通过`Timer`类或`ScheduledExecutorService`实现计时器功能。以下是两种方法的示例及对比: --- #### 1. **使用`Timer`类(简单但存在缺陷)** `Timer`类适合简单任务,但存在以下问题: - **单线程执行**:所有任务共用单个线程,若某任务执行时间过长,会影响后续任务。 - **异常终止风险**:任务抛出未检查异常会导致整个`Timer`线程终止,后续任务无法执行。 **示例代码:** ```java import java.util.Timer; import java.util.TimerTask; public class TimerExample { public static void main(String[] args) { Timer timer = new Timer(); TimerTask task = new TimerTask() { int count = 0; @Override public void run() { System.out.println("任务执行: " + (count++)); if (count >= 5) { timer.cancel(); // 终止计时器 } } }; // 延迟1秒后执行,每隔1秒重复 timer.schedule(task, 1000, 1000); } } ``` --- #### 2. **使用`ScheduledExecutorService`(推荐)** `ScheduledExecutorService`基于线程池设计,支持多任务并发,且能更好处理异常[^3]。 **示例代码:** ```java import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public class ScheduledExecutorExample { public static void main(String[] args) { ScheduledExecutorService executor = Executors.newScheduledThreadPool(1); Runnable task = () -> System.out.println("任务执行: " + System.currentTimeMillis()); // 延迟1秒后首次执行,之后每隔1秒重复 executor.scheduleAtFixedRate(task, 1, 1, TimeUnit.SECONDS); // 若需终止任务 // executor.shutdown(); } } ``` --- ### **关键区别** | 特性 | `Timer`类 | `ScheduledExecutorService` | |-----------------------|--------------------------|------------------------------------| | 线程模型 | 单线程 | 线程池(可配置线程数) | | 异常处理 | 异常导致整个计时器终止 | 异常仅影响当前任务 | | 任务灵活性 | 低 | 高(支持`Callable`和`Future`) | | 适用场景 | 简单、低频任务 | 复杂、高频或需容错的任务 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值