Java 中的 ScheduledExecutorService 接口用来实现延迟执行或者定时执行的任务。在阅读 RocketMQ 源码(release-4.7.1版本)的过程中,发现很多地方都是使用的 ScheduledExecutorService 来实现定时任务。比如,在 broker 启动过程,BrokerController 类中使用 scheduledExecutorService 执行 broker 启动后的定时任务,比如消息消费偏移量 offset 的持久化定时任务,大致如下:
package org.apache.rocketmq.broker;
public class BrokerController {
/**
* 这里省略一些字段
*/
/**
* 使用 Executors.newSingleThreadScheduledExecutor 创建单线程的定时调度任务线程池
*/
private final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryImpl(
"BrokerControllerScheduledThread"));
/**
* broker 启动的初始化方法
*/
public boolean initialize() throws CloneNotSupportedException {
// 省略部分代码
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.getBrokerStats().record();
} catch (Throwable e) {
log.error("schedule record error.", e);
}
}
}, initialDelay, period, TimeUnit.MILLISECONDS);
// broker 启动10s后,默认每隔5s钟持久化一次消费消息偏移量offset信息
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
// 持久化消费偏移量,将 ConsumerOffsetManager 序列化为json文件保存在本地
BrokerController.this.consumerOffsetManager.persist();
} catch (Throwable e) {
log.error("schedule persist consumerOffset error.", e);
}
}
}, 1000 * 10, this.brokerConfig.getFlushConsumerOffsetInterval(), TimeUnit.MILLISECONDS);
this.scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
BrokerController.this.consumerFilterManager.persist();
} catch (Throwable e) {
log.error("schedule persist consumer filter error.", e);
}
}
}, 1000 * 10, 1000 * 10, TimeUnit.MILLISECONDS);
// 后面还有很多.......
}
}
所以觉得有必要学习理解 ScheduledExecutorService。大致看了一下 ScheduledExecutorService 实现类 ScheduledThreadPoolExecutor 的代码,跟我们常用的 ThreadPoolExecutor 线程池类还是有一些差异的,里面有一个基于堆实现的优先队列,后面要再深入学习下,本篇先学习 ScheduledExecutorService 的使用,先学会使用,再分析原理。
本文使用JDK版本:JDK8.
一、ScheduledExecutorService 接口定义
首先,ScheduledExecutorService 是一个接口,它实现了 ExecutorService 接口。
在 ScheduledExecutorService 接口中又新定义了下面四个方法:
1、public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
在指定延迟时间后执行一个 Runnable 任务。因为是 Runnable ,返回值 ScheduledFuture.get() 返回值为 null。
2、 <V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit);
在指定延迟时间后执行一个 Callable 任务。因为是 Callable ,所以 ScheduledFuture.get() 有返回值。
3、ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
创建并执行一个在给定初始延迟 (initialDelay) 后首次启动执行的定时任务,任务执行具有给定的周期 (period);
也就是将在 initialDelay 后开始执行,然后在 initialDelay + period 后执行,接着在 initialDelay + 2 * period 后执行,依此类推。
如果任务的任何一个执行遇到异常,则后续执行都会被取消。相反,任务正常执行的话,只能通过线程池的取消或终止操作来终止该任务。
如果此任务的任何一个执行要花费比其周期更长的时间,则后续的执行将会被推迟,不会出现两个任务同时执行。
4、ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
创建并执行一个在给定初始延迟 (initialDelay) 后首次启动执行的任务,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟(delay)。
如果任务的任一执行遇到异常,就会取消后续任务执行。相反,任务正常执行的话,只能通过线程池的取消或终止操作来终止该任务。


7366

被折叠的 条评论
为什么被折叠?



