JAVA Timer 和 TimerTask 的缺点 作者:马育民 • 2024-03-04 22:09 • 阅读:10033 # 缺点 Java的定时调度可以通过 `Timer` 和 `TimerTask` 来实现,但存在以下缺点: - Timer是 **单线程运行**,即:**串行**,**相互影响**。也就是说,对于同一个 `Timer` 里的多个 `TimerTask` 任务,如果一个 `TimerTask` 任务在执行中,其它 `TimerTask` 即使**到达执行时间**,也只能 **排队等待**。 - 当 `Timer` 中的一个任务 **抛出异常时**,会导致其他所有任务 **不再执行** ### 阿里开发手册 阿里开发手册 也推荐使用 `ScheduledExecutorService` [![](/upload/0/0/1IX4mHgiBnDN.png)](/upload/0/0/1IX4mHgiBnDN.png) # 验证第一个缺点 ### Task1任务类 该类继承 `TimerTask` 类,实现 `run()` 方法,该方法中打印 **开始时间** 和 **结束时间**,中间注释的部分先不用看 ``` package timerstd; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask; public class Task1 extends TimerTask { @Override public void run() { String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务1开始时间:" + printTime); // try { // System.out.println("休眠5秒"); // Thread.sleep(5000); // } catch (InterruptedException e) { // e.printStackTrace(); // } printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务1结束时间:" + printTime); } } ``` ### Task2任务类 该类与 `Task1` 类几乎一样,只是没有中间注释掉的部分 ``` package timerstd; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask; public class Task2 extends TimerTask { @Override public void run() { String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务2开始时间:" + printTime); printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务2结束时间:" + printTime); } } ``` ### 主启动类 创建一个 `Timer` 类对象,并立即启动 `Task1` 和 `Task2` 任务 **注意:** 必须是 **同一个`Timer` 类对象**,上面介绍缺点时有提到 ``` package timerstd; import java.util.Timer; import java.util.TimerTask; public class TimerTest { public static void main(String[] args) { Timer timer = new Timer(); TimerTask tt1 = new Task1(); TimerTask tt2 = new Task2(); // 立即执行 timer.schedule(tt1,0); timer.schedule(tt2,0); } } ``` 执行结果: ``` 任务1开始时间:24-03-07 22-20-29 任务1结束时间:24-03-07 22-20-29 任务2开始时间:24-03-07 22-20-29 任务2结束时间:24-03-07 22-20-29 ``` 能够看到两个任务是同一时刻执行的 ### 改造 Task1 类 将注释的部分去掉,延迟 **5秒钟**,模拟处理业务耗时(处理业务如:查询数据库并处理数据、发邮件、发短信。。。) ``` package timerstd; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask; public class Task1 extends TimerTask { @Override public void run() { String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务1开始时间:" + printTime); try { System.out.println("休眠5秒"); Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务1结束时间:" + printTime); } } ``` 再次运行,执行结果如下: ``` 任务1开始时间:24-03-07 22-22-54 休眠5秒 任务1结束时间:24-03-07 22-22-59 任务2开始时间:24-03-07 22-22-59 任务2结束时间:24-03-07 22-22-59 ``` **关键:**能够看到,**任务1** 运行5秒后才结束,导致 **任务2** 无法立即执行,而是延迟 **5秒** 才执行 # 验证缺点2 ### Task1 该类继承 `TimerTask` 类,实现 `run()` 方法,该方法中打印 **开始时间** 和 **结束时间**,中间由于 `1/0` 抛出 **运行时异常** ``` package timerstd2; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask; public class Task1 extends TimerTask { @Override public void run() { String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务1开始时间:" + printTime); int i = 1/0; printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务1结束时间:" + printTime); } } ``` ### Task2 该类与 `Task1` 类几乎一样,只是没有中间 **抛异常** 的部分 ``` package timerstd2; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask; public class Task2 extends TimerTask { @Override public void run() { String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务2开始时间:" + printTime); printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务2结束时间:" + printTime); } } ``` ### 主启动类 创建一个 `Timer` 类对象,并立即启动 `Task1` 和 `Task2` 任务 **注意:** 必须是 **同一个`Timer` 类对象**,上面介绍缺点时有提到 ``` package timerstd; import java.util.Timer; import java.util.TimerTask; public class TimerTest { public static void main(String[] args) { Timer timer = new Timer(); TimerTask tt1 = new Task1(); TimerTask tt2 = new Task2(); // 立即执行 timer.schedule(tt1,0); timer.schedule(tt2,0); } } ``` 执行结果: ``` 任务1开始时间:24-03-07 23-51-18 Exception in thread "Timer-0" java.lang.ArithmeticException: / by zero at timerstd2.Task1.run(Task1.java:14) at java.util.TimerThread.mainLoop(Timer.java:555) at java.util.TimerThread.run(Timer.java:505) ``` 由于 `Task1` 抛异常,导致其他任务都无法执行 ### 解决 将 `run()` 方法中的代码都用 `try catch` 处理 ##### Task1 ``` package timerstd2.chuli; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask; public class Task1 extends TimerTask { @Override public void run() { try { String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务1开始时间:" + printTime); int i = 1 / 0; printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务1结束时间:" + printTime); }catch (Exception e){ e.printStackTrace(); } } } ``` ##### Task2 ``` package timerstd2.chuli; import java.text.SimpleDateFormat; import java.util.Date; import java.util.TimerTask; public class Task2 extends TimerTask { @Override public void run() { try { String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务2开始时间:" + printTime); printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss").format(new Date()); System.out.println("任务2结束时间:" + printTime); }catch (Exception e){ e.printStackTrace(); } } } ``` 再次执行,结果如下: ``` 任务1开始时间:24-03-07 23-55-15 任务2开始时间:24-03-07 23-55-15 任务2结束时间:24-03-07 23-55-15 java.lang.ArithmeticException: / by zero at timerstd2.chuli.Task1.run(Task1.java:15) at java.util.TimerThread.mainLoop(Timer.java:555) at java.util.TimerThread.run(Timer.java:505) ``` `Task1` 执行报错,但 `Task2` 仍然能正常运行 原文出处:http://malaoshi.top/show_1IX7GUABJuhE.html