All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.dromara.hutool.cron.Scheduler Maven / Gradle / Ivy

There is a newer version: 6.0.0.M3
Show newest version
/*
 * Copyright (c) 2013-2024 Hutool Team and hutool.cn
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.dromara.hutool.cron;

import org.dromara.hutool.core.map.MapUtil;
import org.dromara.hutool.core.thread.ExecutorBuilder;
import org.dromara.hutool.core.thread.ThreadFactoryBuilder;
import org.dromara.hutool.core.text.CharUtil;
import org.dromara.hutool.core.data.id.IdUtil;
import org.dromara.hutool.core.text.StrUtil;
import org.dromara.hutool.cron.listener.TaskListener;
import org.dromara.hutool.cron.listener.TaskListenerManager;
import org.dromara.hutool.cron.pattern.CronPattern;
import org.dromara.hutool.cron.task.InvokeTask;
import org.dromara.hutool.cron.task.RunnableTask;
import org.dromara.hutool.cron.task.Task;
import org.dromara.hutool.log.LogUtil;
import org.dromara.hutool.setting.Setting;

import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.TimeZone;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 任务调度器
* * 调度器启动流程:
* *
 * 启动Timer =》 启动TaskLauncher =》 启动TaskExecutor
 * 
* * 调度器关闭流程:
* *
 * 关闭Timer =》 关闭所有运行中的TaskLauncher =》 关闭所有运行中的TaskExecutor
 * 
* * 其中: * *
 * TaskLauncher:定时器每分钟调用一次(如果{@link Scheduler#isMatchSecond()}为{@code true}每秒调用一次),
 * 负责检查TaskTable是否有匹配到此时间运行的Task
 * 
* *
 * TaskExecutor:TaskLauncher匹配成功后,触发TaskExecutor执行具体的作业,执行完毕销毁
 * 
* * @author Looly * */ public class Scheduler implements Serializable { private static final long serialVersionUID = 1L; private final Lock lock = new ReentrantLock(); /** 定时任务配置 */ protected CronConfig config = new CronConfig(); /** 是否已经启动 */ private boolean started = false; /** 是否为守护线程 */ protected boolean daemon; /** 定时器 */ private CronTimer timer; /** 定时任务表 */ protected TaskTable taskTable = new TaskTable(); /** 启动器管理器 */ protected TaskLauncherManager taskLauncherManager; /** 执行器管理器 */ protected TaskExecutorManager taskExecutorManager; /** 监听管理器列表 */ protected TaskListenerManager listenerManager = new TaskListenerManager(); /** 线程池,用于执行TaskLauncher和TaskExecutor */ protected ExecutorService threadExecutor; // --------------------------------------------------------- Getters and Setters start /** * 设置时区 * * @param timeZone 时区 * @return this */ public Scheduler setTimeZone(final TimeZone timeZone) { this.config.setTimeZone(timeZone); return this; } /** * 获得时区,默认为 {@link TimeZone#getDefault()} * * @return 时区 */ public TimeZone getTimeZone() { return this.config.getTimeZone(); } /** * 设置是否为守护线程
* 如果为true,则在调用{@link #stop()}方法后执行的定时任务立即结束,否则等待执行完毕才结束。默认非守护线程
* 如果用户调用{@link #setThreadExecutor(ExecutorService)}自定义线程池则此参数无效 * * @param on {@code true}为守护线程,否则非守护线程 * @return this * @throws CronException 定时任务已经启动抛出此异常 */ public Scheduler setDaemon(final boolean on) throws CronException { lock.lock(); try { checkStarted(); this.daemon = on; } finally { lock.unlock(); } return this; } /** * 设置自定义线程池
* 自定义线程池时须考虑方法执行的线程是否为守护线程 * * @param threadExecutor 自定义线程池 * @return this * @throws CronException 定时任务已经启动抛出此异常 * @since 5.7.10 */ public Scheduler setThreadExecutor(final ExecutorService threadExecutor) throws CronException { lock.lock(); try { checkStarted(); this.threadExecutor = threadExecutor; } finally { lock.unlock(); } return this; } /** * 是否为守护线程 * * @return 是否为守护线程 */ public boolean isDaemon() { return this.daemon; } /** * 是否支持秒匹配 * * @return {@code true}使用,{@code false}不使用 */ public boolean isMatchSecond() { return this.config.isMatchSecond(); } /** * 设置是否支持秒匹配,默认不使用 * * @param isMatchSecond {@code true}支持,{@code false}不支持 * @return this */ public Scheduler setMatchSecond(final boolean isMatchSecond) { this.config.setMatchSecond(isMatchSecond); return this; } /** * 增加监听器 * * @param listener {@link TaskListener} * @return this */ public Scheduler addListener(final TaskListener listener) { this.listenerManager.addListener(listener); return this; } /** * 移除监听器 * * @param listener {@link TaskListener} * @return this */ public Scheduler removeListener(final TaskListener listener) { this.listenerManager.removeListener(listener); return this; } // --------------------------------------------------------- Getters and Setters end // -------------------------------------------------------------------- shcedule start /** * 批量加入配置文件中的定时任务
* 配置文件格式为: xxx.xxx.xxx.Class.method = * * * * * * * @param cronSetting 定时任务设置文件 * @return this */ public Scheduler schedule(final Setting cronSetting) { if (MapUtil.isNotEmpty(cronSetting)) { String group; for (final Entry> groupedEntry : cronSetting.getGroupedMap().entrySet()) { group = groupedEntry.getKey(); for (final Entry entry : groupedEntry.getValue().entrySet()) { String jobClass = entry.getKey(); if (StrUtil.isNotBlank(group)) { jobClass = group + CharUtil.DOT + jobClass; } final String pattern = entry.getValue(); LogUtil.debug("Load job: {} {}", pattern, jobClass); try { // issue#I5E7BM@Gitee,自定义ID避免重复从配置文件加载 schedule("id_" + jobClass, pattern, new InvokeTask(jobClass)); } catch (final Exception e) { throw new CronException(e, "Schedule [{}] [{}] error!", pattern, jobClass); } } } } return this; } /** * 新增Task,使用随机UUID * * @param pattern {@link CronPattern}对应的String表达式 * @param task {@link Runnable} * @return ID */ public String schedule(final String pattern, final Runnable task) { return schedule(pattern, new RunnableTask(task)); } /** * 新增Task,使用随机UUID * * @param pattern {@link CronPattern}对应的String表达式 * @param task {@link Task} * @return ID */ public String schedule(final String pattern, final Task task) { final String id = IdUtil.fastUUID(); schedule(id, pattern, task); return id; } /** * 新增Task,如果任务ID已经存在,抛出异常 * * @param id ID,为每一个Task定义一个ID * @param pattern {@link CronPattern}对应的String表达式 * @param task {@link Runnable} * @return this */ public Scheduler schedule(final String id, final String pattern, final Runnable task) { return schedule(id, new CronPattern(pattern), new RunnableTask(task)); } /** * 新增Task,如果任务ID已经存在,抛出异常 * * @param id ID,为每一个Task定义一个ID * @param pattern {@link CronPattern}对应的String表达式 * @param task {@link Task} * @return this */ public Scheduler schedule(final String id, final String pattern, final Task task) { return schedule(id, new CronPattern(pattern), task); } /** * 新增Task,如果任务ID已经存在,抛出异常 * * @param id ID,为每一个Task定义一个ID * @param pattern {@link CronPattern} * @param task {@link Task} * @return this */ public Scheduler schedule(final String id, final CronPattern pattern, final Task task) { taskTable.add(id, pattern, task); return this; } /** * 移除Task * * @param id Task的ID * @return this */ public Scheduler deschedule(final String id) { descheduleWithStatus(id); return this; } /** * 移除Task,并返回是否移除成功 * * @param id Task的ID * @return 是否移除成功,{@code false}表示未找到对应ID的任务 * @since 5.7.17 */ public boolean descheduleWithStatus(final String id) { return this.taskTable.remove(id); } /** * 更新Task执行的时间规则 * * @param id Task的ID * @param pattern {@link CronPattern} * @return this * @since 4.0.10 */ public Scheduler updatePattern(final String id, final CronPattern pattern) { this.taskTable.updatePattern(id, pattern); return this; } /** * 获取定时任务表,注意此方法返回非复制对象,对返回对象的修改将影响已有定时任务 * * @return 定时任务表{@link TaskTable} * @since 4.6.7 */ public TaskTable getTaskTable() { return this.taskTable; } /** * 获得指定id的{@link CronPattern} * * @param id ID * @return {@link CronPattern} * @since 3.1.1 */ public CronPattern getPattern(final String id) { return this.taskTable.getPattern(id); } /** * 获得指定id的{@link Task} * * @param id ID * @return {@link Task} * @since 3.1.1 */ public Task getTask(final String id) { return this.taskTable.getTask(id); } /** * 是否无任务 * * @return true表示无任务 * @since 4.0.2 */ public boolean isEmpty() { return this.taskTable.isEmpty(); } /** * 当前任务数 * * @return 当前任务数 * @since 4.0.2 */ public int size() { return this.taskTable.size(); } /** * 清空任务表 * @return this * @since 4.1.17 */ public Scheduler clear() { this.taskTable = new TaskTable(); return this; } // -------------------------------------------------------------------- shcedule end /** * @return 是否已经启动 */ public boolean isStarted() { return this.started; } /** * 启动 * * @param isDaemon 是否以守护线程方式启动,如果为true,则在调用{@link #stop()}方法后执行的定时任务立即结束,否则等待执行完毕才结束。 * @return this */ public Scheduler start(final boolean isDaemon) { this.daemon = isDaemon; return start(); } /** * 启动 * * @return this */ public Scheduler start() { lock.lock(); try { checkStarted(); if(null == this.threadExecutor){ // 无界线程池,确保每一个需要执行的线程都可以及时运行,同时复用已有线程避免线程重复创建 this.threadExecutor = ExecutorBuilder.of().useSynchronousQueue().setThreadFactory(// ThreadFactoryBuilder.of().setNamePrefix("hutool-cron-").setDaemon(this.daemon).build()// ).build(); } this.taskLauncherManager = new TaskLauncherManager(this); this.taskExecutorManager = new TaskExecutorManager(this); // Start CronTimer timer = new CronTimer(this); timer.setDaemon(this.daemon); timer.start(); this.started = true; } finally { lock.unlock(); } return this; } /** * 停止定时任务
* 此方法调用后会将定时器进程立即结束,如果为守护线程模式,则正在执行的作业也会自动结束,否则作业线程将在执行完成后结束。
* 此方法并不会清除任务表中的任务,请调用{@link #clear()} 方法清空任务或者使用{@link #stop(boolean)}方法可选是否清空 * * @return this */ public Scheduler stop() { return stop(false); } /** * 停止定时任务
* 此方法调用后会将定时器进程立即结束,如果为守护线程模式,则正在执行的作业也会自动结束,否则作业线程将在执行完成后结束。 * * @param clearTasks 是否清除所有任务 * @return this * @since 4.1.17 */ public Scheduler stop(final boolean clearTasks) { lock.lock(); try { if (!started) { throw new IllegalStateException("Scheduler not started !"); } // 停止CronTimer this.timer.stopTimer(); this.timer = null; //停止线程池 this.threadExecutor.shutdown(); this.threadExecutor = null; //可选是否清空任务表 if(clearTasks) { clear(); } // 修改标志 started = false; } finally { lock.unlock(); } return this; } /** * 检查定时任务是否已经启动 * * @throws CronException 已经启动则抛出此异常 */ private void checkStarted() throws CronException{ if (this.started) { throw new CronException("Scheduler already started!"); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy