文章目录
-
-
-
- 一、JDK 自带定时器
- 二、JDK 自带定时器的实现方式
- 三、JDK 自带定时器的问题描述
- 四、针对问题的解决方案
-
-
一、JDK 自带定时器
-
在
JDK
的工具类中有一个Timer
类用于定时任务的业务实现 -
根据
Timer.schedule
的重载方法,可以实现不同机制的任务调度 -
接下来演示不同机制都具体表现以及存在都问题和解决方案
二、JDK 自带定时器的实现方式
-
代码实现
/*** Copyright (C), 1998-2021, Shenzhen Rambo Technology Co., Ltd* JDK 自带调度任务** 弊端:* 任务调度都采用同一线程池同一线程* 如果一旦某个任务响应时间过长,将会导致线程阻塞(单线程执行多个任务)** @author Rambo* @date 2021/2/22 17:05* @since 1.0.0.1*/ @Slf4j public class TimerScheduleTasks { // 定义任务调度器实例对象private static final Timer timer = new Timer();public static void main(String[] args) { timer1();timer2();timer3();timer4();}/*** 方法一:* schedule(TimerTask task, long delay, long period);* 设定指定任务 task 在指定延迟 delay 后进行固定延迟 period 的执行* 0ms之后开始执行,每隔 1000 ms执行一次** @author Rambo* @date 2021/2/22 17:19*/public static void timer1(){ timer.schedule(new TimerTask(){ @SneakyThrows@Overridepublic void run(){ log.info("-------------> 调度线程名称:[{}],被调度方法名称:[TimerScheduleTasks.timer1()],执行频率:1秒/次,当前时间:[{}]", Thread.currentThread().getName(), DateUtil.now());// 模拟业务处理时长TimeUnit.SECONDS.sleep(5);}}, 0 , 1000);}/*** 方法二:* schedule(TimerTask task, Date time);* 设定指定任务 task 在指定时间 time 后执行一次* 5ms 后执行一次 run 方法后结束,执行完成后线程处于挂起等待状态** @author Rambo* @date 2021/2/22 17:19*/public static void timer2(){ timer.schedule(new TimerTask(){ @SneakyThrows@Overridepublic void run(){ log.info("-------------> 调度线程名称:[{}],被调度方法名称:[TimerScheduleTasks.timer2()],当前时间:[{}]", Thread.currentThread().getName(), DateUtil.now());// 模拟业务处理时长TimeUnit.SECONDS.sleep(5);}}, 5000);}/*** 方法三:* scheduleAtFixedRate(TimerTask task, long delay, long period);* 设定指定任务 task 在指定延迟 delay 后进行固定频率 period 的执行* 0ms 后,每隔 2s 执行一次* @author Rambo* @date 2021/2/22 17:39*/public static void timer3() { timer.scheduleAtFixedRate(new TimerTask() { @SneakyThrows@Overridepublic void run() { log.info("-------------> 调度线程名称:[{}],被调度方法名称:[TimerScheduleTasks.timer3()],执行频率:2秒/次,当前时间:[{}]", Thread.currentThread().getName(), DateUtil.now());// 模拟业务处理时长TimeUnit.SECONDS.sleep(5);}}, 0, 2000);}/*** 方法四:* scheduleAtFixedRate(TimerTask task, Date firstTime, long period);* 设定指定的任务 task 在指定的时间 firstTime 开始进行重复的固定速率 period 执行* 每天在预设时间去执行,执行完成后线程处于挂起等待状态** @author Rambo * @date 2021/2/22 17:45*/public static void timer4(){ Calendar calendar = Calendar.getInstance();// 控制时calendar.set(Calendar.HOUR_OF_DAY , 17);// 控制分calendar.set(Calendar.MINUTE , 51);// 控制秒calendar.set(Calendar.SECOND , 0);Date time = calendar.getTime();SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss");log.info("任务计划执行时间:[{}]", format.format(time));timer.scheduleAtFixedRate(new TimerTask(){ @SneakyThrows@Overridepublic void run(){ log.info("-------------> 调度线程名称:[{}],被调度方法名称:[TimerScheduleTasks.timer4()],执行频率:每天固定时间执行,当前时间:[{}]", Thread.currentThread().getName(), DateUtil.now());// 模拟业务处理时长TimeUnit.SECONDS.sleep(5);}// 这里设定将延时每天固定执行}, time, 1000 * 60 * 60 * 24);} }
三、JDK 自带定时器的问题描述
-
任务调度都采用同一线程池同一线程
-
如果一旦某个任务响应时间过长,将会导致线程阻塞(单线程执行多个任务)
四、针对问题的解决方案
-
方案一(不推荐:只解决线程问题,没有解决线程阻塞问题)
-
由于上述案例代码中,所有的调度任务都采用同一个
Timer
实例对象,所以多个任务同时协作时,使用的是同一个线程... ... public static void timer2(){ // 每个调度任务都用新的实例去创建Timer timer = new Timer();timer.schedule(new TimerTask(){ @SneakyThrows@Overridepublic void run(){ log.info("-------------> 调度线程名称:[{}],被调度方法名称:[TimerScheduleTasks.timer2()],当前时间:[{}]", Thread.currentThread().getName(), DateUtil.now());// 模拟业务处理时长TimeUnit.SECONDS.sleep(5);}}, 5000); } ... ...
-
通过以上分析,提出的解决方案是在触发每一个调度任务都采用新的
Timer
实例,则可以采用不同的线程去调度 -
运行效果
弊端
- 每一次调度都采用新的
Timer
实例对象来创建,可以解决同一线程问题,但是阻塞问题并未解决,原因是阻塞方法还是只能通过这个实例来调度
-
-
方案二(推荐)
-
采用
ThreadPoolTaskExecutor
对象自定义线程池的方式结合@Async
注解来实现 -
采用
ThreadPoolTaskScheduler
对象自定义线程池的方式结合@Scheduled
注解来实现 -
以上介绍的两种方式,将在后续文章给出具体实现
-