对码当歌,猿生几何?

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.     * The timer thread.

  11.     */

  12. private TimerThread thread = new TimerThread(queue);


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




  1. package com.zhy.concurrency.timer;


  2. import java.util.Timer;

  3. import java.util.TimerTask;


  4. public class TimerTest

  5. {

  6. private static long start;


  7. public static void main(String[] args) throws Exception

  8. {


  9. TimerTask task1 = new TimerTask()

  10. {

  11. @Override

  12. public void run()

  13. {


  14. System.out.println("task1 invoked ! "

  15. + (System.currentTimeMillis() - start));

  16. try

  17. {

  18. Thread.sleep(3000);

  19. } catch (InterruptedException e)

  20. {

  21. e.printStackTrace();

  22. }


  23. }

  24. };

  25. TimerTask task2 = new TimerTask()

  26. {

  27. @Override

  28. public void run()

  29. {

  30. System.out.println("task2 invoked ! "

  31. + (System.currentTimeMillis() - start));

  32. }

  33. };

  34. Timer timer = new Timer();

  35. start = System.currentTimeMillis();

  36. timer.schedule(task1, 1000);

  37. timer.schedule(task2, 3000);


  38. }

  39. }


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


  1. task1 invoked ! 1000

  2. task2 invoked ! 4000

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




  1. package com.zhy.concurrency.timer;


  2. import java.util.TimerTask;

  3. import java.util.concurrent.Executors;

  4. import java.util.concurrent.ScheduledExecutorService;

  5. import java.util.concurrent.TimeUnit;


  6. public class ScheduledThreadPoolExecutorTest

  7. {

  8. private static long start;


  9. public static void main(String[] args)

  10. {

  11. /**

  12. * 使用工厂方法初始化一个ScheduledThreadPool

  13. */

  14. ScheduledExecutorService newScheduledThreadPool = Executors

  15. .newScheduledThreadPool(2);


  16. TimerTask task1 = new TimerTask()

  17. {

  18. @Override

  19. public void run()

  20. {

  21. try

  22. {


  23. System.out.println("task1 invoked ! "

  24. + (System.currentTimeMillis() - start));

  25. Thread.sleep(3000);

  26. } catch (Exception e)

  27. {

  28. e.printStackTrace();

  29. }


  30. }

  31. };


  32. TimerTask task2 = new TimerTask()

  33. {

  34. @Override

  35. public void run()

  36. {

  37. System.out.println("task2 invoked ! "

  38. + (System.currentTimeMillis() - start));

  39. }

  40. };

  41. start = System.currentTimeMillis();

  42. newScheduledThreadPool.schedule(task1, 1000, TimeUnit.MILLISECONDS);

  43. newScheduledThreadPool.schedule(task2, 3000, TimeUnit.MILLISECONDS);

  44. }

  45. }


输出结果:



<code class="language-java hljs" style="color:rgb(171,178,191);background-color:rgb(40,44,52);font-f