Timer/ScheduledThreadPoolExecutor都是是JDK提供的api
import lombok.extern.slf4j.Slf4j;
import java.util.Timer;
import java.util.TimerTask;
@Slf4j
public class TimerDemo {
public static void main(String[] args) {
Timer timer = new Timer();
log.info("--- 提交延时任 ---");
timer.schedule(new TimerTask() {
@Override
public void run() {
log.info("--- 执行延迟任务 ---");
}
},5000);
}
}
执行结果与预期一致
nextExecutionTime
属性,代表下一次任务执行的时间,在提交任务的时候会计算出nextExecutionTime
值。TaskQueue
对象,用来保存TimerTask
任务的,会根据nextExecutionTime来排序,保证能够快速获取到最早需要被执行的延迟任务。TimerThread
,这个线程就跟DelayQueue demo中开启的线程作用是一样的,用来执行到了延迟时间的任务。所以总的来看,Timer有点像整体封装了DelayQueue demo中的所有东西,让用起来简单点。
虽然Timer用起来比较简单,但是在阿里规范中是不推荐使用的,主要是有以下几点原因:
- Timer使用单线程来处理任务,长时间运行的任务会导致其他任务的延时处理
- Timer没有对运行时异常进行处理,一旦某个任务触发运行时异常,会导致整个Timer崩溃,不安全
由于Timer在使用上有一定的问题,所以在JDK1.5版本的时候提供了ScheduledThreadPoolExecutor,这个跟Timer的作用差不多,并且他们的方法的命名都是差不多的,但是ScheduledThreadPoolExecutor解决了单线程和异常崩溃等问题。
@Slf4j
public class ScheduledThreadPoolExecutorDemo {
public static void main(String[] args) {
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(
5,
new ThreadPoolExecutor.CallerRunsPolicy()
);
log.info("--- 提交延时任务 ---");
executor.schedule(() -> log.info("--- 执行延时任务 ---") ,5, TimeUnit.SECONDS);
}
}
拒绝策略
new ThreadPoolExecutor.CallerRunsPolicy()
参考:
执行结果从时间来看与预期结果一致。
ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,也就是继承了线程池,所以可以有很多个线程来执行任务。
ScheduledThreadPoolExecutor在构造的时候会传入一个DelayedWorkQueue阻塞队列,所以线程池内部的阻塞队列是DelayedWorkQueue。
在提交延迟任务的时候,任务会被封装一个任务会被封装成ScheduledFutureTask对象,然后放到DelayedWorkQueue阻塞队列中。
ScheduledFutureTask
实现了前面提到的Delayed
接口,所以其实可以猜到DelayedWorkQueue
会根据ScheduledFutureTask
对于Delayed接口的实现来排序,所以线程能够获取到最早到延迟时间的任务。
当线程从DelayedWorkQueue
中获取到需要执行的任务之后就会执行任务。
http://blog.xqlee.com/article/2501211658348107.html