文章目录

  • 概述
  • 问题复现
  • 源码分析
    • 源码分析
  • How to Fix
    • 方法一 : run方法内最好使用try-catch结构捕捉可能的异常,不要把异常抛到run方法之外
    • 方法二: ScheduledThreadPoolExecutor (推荐)
  • 小结


概述

先说结论 当一个Timer运行多个TimerTask时,只要其中一个TimerTask在执行中向run方法外抛出了异常,则其他任务也会自动终止

我们看插件的提示


问题复现


import java.util.Timer;
import java.util.TimerTask;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/11/21 20:28* @mark: show me the code , change the world*/
public class TimerTest {// 定时器static Timer timer = new Timer();public static void main(String[] args) {// 任务1 , 延迟500ms执行  1秒执行一次timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Task1 Running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 模拟发生异常throw new RuntimeException();}},500,1000);// 任务2, 延迟1000ms执行  1秒执行一次timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("Task2 Running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}},1000,1000);}
}

如上代码首先添加了第一个任务,让其在500ms后执行。然后添加了第二个任务在1s后执行,我们期望当第一个任务输出Task1 Running后,等待1s,第二个任务输出Task1 Running,,然后循环,每隔1秒执行一次。

但是执行代码后,输出结果为


源码分析

Timer的原理模型如下

  • TaskQueue是一个由平衡二叉树堆实现的优先级队列,每个Timer对象内部有一个TaskQueue队列。用户线程调用Timer的schedule方法就是把TimerTask任务添加到TaskQueue队列。在调用schedule方法时,long delay参数用来指明该任务延迟多少时间执行。

  • ·TimerThread是具体执行任务的线程,它从TaskQueue队列里面获取优先级最高的任务进行执行。需要注意的是,只有执行完了当前的任务才会从队列里获取下一个任务,而不管队列里是否有任务已经到了设置的delay时间。一个Timer只有一个TimerThread线程,所以可知Timer的内部实现是一个多生产者-单消费者模型


源码分析

从该实现模型我们知道,要探究上面的问题只需研究TimerThread的实现就可以了。TimerThread的run方法的主要逻辑代码如下。

/*** This "helper class" implements the timer's task execution thread, which* waits for tasks on the timer queue, executions them when they fire,* reschedules repeating tasks, and removes cancelled tasks and spent* non-repeating tasks from the queue.*/
class TimerThread extends Thread {/*** This flag is set to false by the reaper to inform us that there* are no more live references to our Timer object.  Once this flag* is true and there are no more tasks in our queue, there is no* work left for us to do, so we terminate gracefully.  Note that* this field is protected by queue's monitor!*/boolean newTasksMayBeScheduled = true;/*** Our Timer's queue.  We store this reference in preference to* a reference to the Timer so the reference graph remains acyclic.* Otherwise, the Timer would never be garbage-collected and this* thread would never go away.*/private TaskQueue queue;TimerThread(TaskQueue queue) {this.queue = queue;}public void run() {try {mainLoop();} finally {// Someone killed this Thread, behave as if Timer cancelledsynchronized(queue) {newTasksMayBeScheduled = false;queue.clear();  // Eliminate obsolete references}}}/*** The main timer loop.  (See class comment.)*/private void mainLoop() {while (true) {try {TimerTask task;boolean taskFired;// 从队列里面获取任务时要加锁synchronized(queue) {.........if (taskFired)  // Task fired; run it, holding no lockstask.run();// 执行任务} catch(InterruptedException e) {}}}
}

当任务在执行过程中抛出InterruptedException之外的异常时,唯一的消费线程就会因为抛出异常而终止,那么队列里的其他待执行的任务就会被清除


How to Fix

方法一 : run方法内最好使用try-catch结构捕捉可能的异常,不要把异常抛到run方法之外

所以在TimerTask的run方法内最好使用try-catch结构捕捉可能的异常,不要把异常抛到run方法之外。


方法二: ScheduledThreadPoolExecutor (推荐)

其实要实现Timer功能,使用ScheduledThreadPoolExecutor的schedule是比较好的选择。如果ScheduledThreadPoolExecutor中的一个任务抛出异常,其他任务则不受影响。

public class TimerTest {public static void main(String[] args) throws InterruptedException {ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(1);scheduledThreadPoolExecutor.schedule(()->{System.out.println("Task1 Running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 模拟发生异常throw new RuntimeException();},1, TimeUnit.SECONDS);scheduledThreadPoolExecutor.schedule(()->{System.out.println("Task2 Running");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}// 关闭线程池scheduledThreadPoolExecutor.shutdown();},1, TimeUnit.SECONDS);}}

之所以ScheduledThreadPoolExecutor的其他任务不受抛出异常的任务的影响,是因为在ScheduledThreadPoolExecutor中的ScheduledFutureTask任务中catch掉了异常


小结

ScheduledThreadPoolExecutor是并发包提供的组件,其提供的功能包含但不限于Timer。Timer是固定的多线程生产单线程消费,但是ScheduledThreadPoolExecutor是可以配置的,既可以是多线程生产单线程消费也可以是多线程生产多线程消费,所以在日常开发中使用定时器功能时应该优先使用ScheduledThreadPoolExecutor

Java Review - 使用Timer时需要注意的事情相关推荐

  1. java捕获定时器抛出的异常_详细了解Java中定时器Timer的使用及缺陷分析

    在需要定时并且周期执行任务时,在最初的JAVA工具类库中,Timer可以实现任务的定时周期执行的需求,不过有一定的缺陷,比如,Timer是基于绝对时间而非相对时间,因此Timer对系统时钟比较敏感,本 ...

  2. Java Review - 使用Event Bus实现目录变化的监控

    文章目录 Pre 需求 Event Bus案例实战 小结 Pre Java Review - Java进程内部的消息中间件_Event Bus设计模式 需求 监控文件的实时变化 , 就这一句话??? ...

  3. Java Review - 并发编程_ 回环屏障CyclicBarrier原理源码剖析

    文章目录 Pre 小Demo 类图结构 CyclicBarrier核心方法源码解读 int await() int await(long timeout, TimeUnit unit) int dow ...

  4. Java Review - 并发编程_ScheduledThreadPoolExecutor原理源码剖析

    文章目录 概述 类结构 核心方法&源码解析 schedule(Runnable command, long delay,TimeUnit unit) scheduleWithFixedDela ...

  5. Java Review - 并发编程_ArrayBlockingQueue原理源码剖析

    文章目录 概述 类图结构 构造函数 主要方法源码解析 offer操作 put操作 poll操作 take操作 peek操作 size 小结 概述 Java Review - 并发编程_LinkedBl ...

  6. Java Review - 并发编程_LinkedBlockingQueue原理源码剖析

    文章目录 概述 类图结构 主要方法 offer操作 概述 Java Review - 并发编程_ConcurrentLinkedQueue原理&源码剖析 介绍了使用CAS算法实现的非阻塞队列C ...

  7. Java Review - 并发编程_读写锁ReentrantReadWriteLock的原理源码剖析

    文章目录 ReentrantLock VS ReentrantReadWriteLock 类图结构 非公平的读写锁实现 写锁的获取与释放 void lock() void lockInterrupti ...

  8. Java Review - 并发编程_原子操作类LongAdder LongAccumulator剖析

    文章目录 概述 小Demo 源码分析 重要的方法 long sum() reset sumThenReset longValue() add(long x) longAccumulate(long x ...

  9. Java Review - LinkedHashMap LinkedHashSet 源码解读

    文章目录 Pre 概述 数据结构 类继承关系 构造函数 方法 get() put() remove() LinkedHashSet 使用案例 - FIFO策略缓存 Pre Java Review - ...

最新文章

  1. P4619 [SDOI2018]旧试题(莫比乌斯反演,建图优化三重枚举,三元环计数,神仙好题,超级清晰易懂)
  2. 141. Linked List Cycle
  3. 用Java实现天天酷跑(附源码),只能用牛逼来形容了!
  4. hibernate数据的三种存在状态(只与事务有关)
  5. Opencv图像保存到电脑及显示
  6. IE6下透明PNG图片的显示
  7. 树莓派红外避障小车python_基于树莓派的环保“捡垃圾”机器人小车(避障、摄像、红外、WIFI)...
  8. Springboot2.x 拦截器
  9. 【java】读取html乱码
  10. Linux mmc驱动框架(4)——卡检测及初始化
  11. 这一年炼就的底层内功修养
  12. es文件浏览器android看不到文件,es文件浏览器搜索不到电脑解决办法 es文件浏览器找不到电脑...
  13. 横河变送器EJA110E-JMS4J-919EA
  14. jsp中获取不到servlet中的ArrayList
  15. 一个优秀的UI界面应该是什么样的
  16. 用表格做出的阴影扇形图
  17. 海外出货量占比超七成,海外市场决定小米手机的未来
  18. linux sata驱动加载硬盘顺序,linux安装前必备知识
  19. 清华大学教授张长水:基于小样本学习的计算机视觉应用
  20. 判断一个序列是否可由给定序列通过栈操作获得

热门文章

  1. 隐藏画质代码_「图」画质修改工具ReShade放出4.5.0更新:改进对Vulkan等较新API的支持...
  2. Andriod:安卓线程实现页面的自动跳转
  3. android 之Activity间的相互跳转(通过intent构造函数)
  4. opencv 显示图片(直接)
  5. super().__init__()理解(三)
  6. 算术编码例题详解_百分数与百分点区别详解
  7. 12. Leetcode 350. 两个数组的交集 II (数组-分离双指针)
  8. 推荐系统笔记: 基于邻居的协同过滤问题 中的降维
  9. pyecharts应用3 Faker函数库
  10. pytorch学习笔记 torchnn.ModuleList