ScheduledExecutorService延时线程池的简单使用
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |
文章目录
- 一、Timer
- 1、代码测试
- 2、总结
- 二、ScheduledExecutorService
- 1、简单使用
- 2、源码分析
一、Timer
在java.util包下有一个Timer类,用于实现定时任务
1、代码测试
代码实现步骤:
1、创建一个timer对象
2、调用timer对象的schedule多态方法,根据传入参数的不同,选择以何种方式执行任务
- 第一个测试的是只执行一次的任务
- 第二个测试的是周期性执行的任务
测试结果:
2、总结
在我们实例化Timer类的对象的时候,他会去创建一个线程,用于后期任务的执行。这是使用Timer类执行定时任务的一大特性。如果我们的场景出现了一个线程出现异常,但是其他任务还能继续执行其他任务,那么使用Timer必然是不符合要求的。
在JDK1.5之后,Doug Lea在Java并发包下引入了ScheduledExecutorService,其拥有线程池的复用线程的特性,支持多线程,其中一个线程挂掉,也不会影响其他线程运行(出现异常的线程会被线程池的waiters队列丢弃,即变成一个null任务的线程)。具体线程池是如何丢弃的细节请参考:浅谈ThreadPoolExecutor线程池底层源码
二、ScheduledExecutorService
根据类图可以得出ScheduledExecutorService是ThreadPoolExecutor的子类,其拥有线程池的特性
1、简单使用
四个测试案例分别对应ScheduledExecutorService接口中的4个API。对应的效果写在了上面代码的注释即log中
2、源码分析
和线程池的分析顺序大致相同
1、以scheduleAtFixedRate方法为入口(其余三个方法的差不多,仅仅是不同的方法设置了不同的参数,用于后期条件判断)。注意这里的sft.outerTask = t,后文会再次出现
2、调用对应的delayedExecute方法
3、将任务添加到队列中的步骤已经完成,接下来就是当符合了延时的条件,延时队列就会弹出对应的线程任务,调用对应的run方法
4、调用run方法
- 判定是周期性的还是一次性的:这里的判断结果根据我们是调用何种API(接口中的四个方法)决定的
- 设置下次执行时间:周期性任务的有两个API,一个是scheduleAtFixedRate,一个是scheduleWithFixedDelay,两个方法传入的值都是正数,但是后面一个方法会将传入的值封装为一个负数(前面的方法值不变),这里的值就是根据这个封装的结果来决定的
- 将下次需要执行的任务添加到队列中:下一次需要执行的任务就是第一步设置的sft.outerTask = t,即把自己再次添加到队列中
5、周期性任务的下一次执行操作,
补充:延时线程池的实现其实也不难理解,内部维护了DelayQueue队列,而DelayQueue队列内部又采用优先队列 PriorityQueue,PriorityQueue其内部实现是基于二叉堆的数据结构(对并发包下常见的队列不太熟悉的可以参考我之前的博客:七个常见队列的简单学习,这些队列的概念一定要理解清楚,这对你你整个并发知识的学习至关重要)。加之DelayQueue内部的元素可以基于Comparable 接口,按照规则进行排序,即达到了指定顺序的出队列的效果。
目前有很多的任务调度的组件,除了上面说到的Timer和ScheduledExecutorService,还有Spring Task、Quartz、xxl-job、Elastic-job等等。不过我认为学好JDK自带的组件是研究其他组件的基础!!!
阿里云国内75折 回扣 微信号:monov8 |
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6 |