ScheduledExecutorService的主要作用就是可以将定时任务与线程池功能结合使用
下面是一个简单的例子,功能就是每隔1秒中执行一下run里面的方法
public class ScheduledExecutorServiceTest {
public static void main(String[] args) {
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println("this is a test");
}
}, 1000, 1000, TimeUnit.MILLISECONDS);
}
}
但是在实际工作中,第一我们可能有多个定时任务需要管理,第二对于定时任务我们可能希望在不要重启服务的情况下就可以对其进行关闭、启动、修改间隔时间的操作。那么结合spring我们可以怎么做呢,本人在这里分享一个自己做的例子,希望可以起到抛砖引玉的作用。
首先我们定义一个ScheduledTask类,主要是配置定时任务的方法、间隔时间等。
public class ScheduledTask {
/** 需要执行方法的线程 */
private Runnable runnable;
/** 唯一的id用于增删改 */
private String taskId;
/** 定时任务需要执行的方法类 */
private String className;
/** 定时任务需要执行的方法 */
private String method;
/** 首次执行等待时间 */
private long initialDelay;
/** 间隔时间 */
private long delay;
public ScheduledTask(){
}
public ScheduledTask(String taskId, String className, String method,long initialDelay,long delay) {
this.taskId = taskId;
this.className = className;
this.method = method;
this.initialDelay = initialDelay;
this.delay = delay;
// 在创建实例的时候,初始化线程类,通过反射获取要执行的类与方法,目前没有加参数,大家可以自行扩展
runnable = () -> {
Object bean = SpringContextUtil.getBean(className);
try {
Method method1 = bean.getClass().getMethod(method);
method1.invoke(bean,null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
};
}
get set方法省略
}
编写接口
public interface ScheduledService {
/**
* 添加一个任务
* @author Eric
* @date 16:14 2019/3/12
* @params runnable
* @params taskId
* @params initialDelay 初次等待 毫秒
* @params delay 间隔时间 毫秒
* @throws
* @return boolean
**/
boolean addTask(ScheduledTask scheduledTask);
/**
* 修改一个任务
* @author Eric
* @date 16:14 2019/3/12
* @params runnable
* @params taskId
* @params initialDelay 初次等待 毫秒
* @params delay 间隔时间 毫秒
* @throws
* @return boolean
**/
boolean updateTask(ScheduledTask scheduledTask) throws InterruptedException;
/**
* 移除一个任务
* @author Eric
* @date 16:14 2019/3/12
* @params taskId
* @throws
* @return boolean
**/
boolean remove(String taskId);
/**
* 关闭定时任务服务
* @author Eric
* @date 16:14 2019/3/12
* @throws
* @return void
**/
void shutdown();
/**
* 初始化定时任务服务
* @author Eric
* @date 16:15 2019/3/12
* @throws
* @return void
**/
void init() throws Exception;
}
实现接口:
@Service
public class ScheduledServiceImpl implements ScheduledService, InitializingBean {
private static ScheduledExecutorService service;
private static Map futureMap = new ConcurrentHashMap<>();
// 这个方法是 InitializingBean 中的方法重写,目的是再spring载入了配置信息后再初始化数据,这样再实际工作中可以通过查// 询数据库获取定时任务信息,然后一次性的载入到定时任务服务中
@Override
public void afterPropertiesSet() throws Exception {
// 初始化
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("schedule-pool-%d").setDaemon(true).build();
if (service != null) {
service.shutdown();
}
service = new ScheduledThreadPoolExecutor(1, threadFactory, new ThreadPoolExecutor.AbortPolicy());
}
@Override
public boolean addTask(ScheduledTask scheduledTask) {
if (futureMap.get(scheduledTask.getTaskId()) != null) {
return false;
}
// 这里将任务放入定时服务中
ScheduledFuture> scheduledFuture = service.scheduleWithFixedDelay(scheduledTask.getRunnable()
, scheduledTask.getInitialDelay(), scheduledTask.getDelay(), TimeUnit.MILLISECONDS);
futureMap.put(scheduledTask.getTaskId(),scheduledFuture);
return true;
}
@Override
public boolean updateTask(ScheduledTask scheduledTask) throws InterruptedException {
if (futureMap.get(scheduledTask.getTaskId()) == null) {
return false;
}
// 先停止
remove(scheduledTask.getTaskId());
// 再添加
addTask(scheduledTask);
return true;
}
@Override
public boolean remove(String taskId) {
if (futureMap.get(taskId) == null) {
return false;
}
ScheduledFuture scheduledFuture = futureMap.get(taskId);
scheduledFuture.cancel(false);
futureMap.remove(taskId);
return true;
}
@Override
public void shutdown() {
service.shutdown();
}
@Override
public void init() throws Exception {
afterPropertiesSet();
}
}
好 到此,一个最基本的可以动态起 停 修改定时任务的功能就完成了,我们写一个测试类来测试一下。
先写一个测试服务:
@Service("scheduledTestService")
public class ScheduledTestService {
public void test1(){
System.out.println("hello ScheduledTest");
}
public void test2(){
System.out.println("hello ScheduledTest2");
}
}
写一个spring的junit测试类,主要方法如下
@Resource
private ScheduledService scheduledService;
@Test
public void add() throws InterruptedException {
scheduledService.addTask(new ScheduledTask("3","scheduledTestService","test1",1000,1000));
Thread.sleep(5000);
scheduledService.addTask(new ScheduledTask("4"
,"scheduledTestService","test2",1000,3000));
Thread.sleep(5000);
scheduledService.remove("3");
Thread.sleep(5000);
}
运行结果如下:
hello ScheduledTest
hello ScheduledTest
hello ScheduledTest
hello ScheduledTest
hello ScheduledTest2
hello ScheduledTest
hello ScheduledTest
hello ScheduledTest
hello ScheduledTest2
hello ScheduledTest
hello ScheduledTest2
hello ScheduledTest2
工具类:SpringContextUtil
@Component
public class SpringContextUtil implements ApplicationContextAware {
/** Spring应用上下文环境 */
@Autowired
private static ApplicationContext applicationContext;
/**
* 实现ApplicationContextAware接口的回调方法。设置上下文环境
* @param applicationContext
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextUtil.applicationContext = applicationContext;
}
/**
* @return ApplicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 获取对象
*
* @param name
* @return Object
* @throws BeansException
*/
public static Object getBean(String name) throws BeansException {
return applicationContext.getBean(name);
}
}
下一步,我们将加入起始和结束时间 时间可以在时间周期内定时执行的功能
推荐:史上最全的java开发工具类 地址:https://github.com/EricLoveMia/JavaTools