Java 如何停止使用@Scheduled 注释启动的计划任务?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/44644141/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to stop a scheduled task that was started using @Scheduled annotation?
提问by Sach141
I have created a simple scheduled task using Spring Framework's @Scheduled
annotation.
我使用 Spring Framework 的@Scheduled
注释创建了一个简单的计划任务。
@Scheduled(fixedRate = 2000)
public void doSomething() {}
Now I want to stop this task, when no longer needed.
现在我想在不再需要时停止这项任务。
I know there could be one alternative to check one conditional flag at the start of this method, but this will not stop execution of this method.
我知道可能有一种替代方法可以在此方法开始时检查一个条件标志,但这不会停止此方法的执行。
Is there anything Spring provides to stop @Scheduled
task ?
Spring 提供什么来停止@Scheduled
任务?
采纳答案by Mahesh
Option 1: Using a post processor
选项 1:使用后处理器
Supply ScheduledAnnotationBeanPostProcessor
and explicitly invoke postProcessBeforeDestruction(Object bean, String beanName)
, for the bean whose scheduling should be stopped.
为应该停止调度的 bean提供ScheduledAnnotationBeanPostProcessor
并显式调用postProcessBeforeDestruction(Object bean, String beanName)
。
Option 2: Maintaining a map of target beans to its Future
选项 2:维护目标 bean 到它的 Future 的映射
private final Map<Object, ScheduledFuture<?>> scheduledTasks =
new IdentityHashMap<>();
@Scheduled(fixedRate = 2000)
public void fixedRateJob() {
System.out.println("Something to be done every 2 secs");
}
@Bean
public TaskScheduler poolScheduler() {
return new CustomTaskScheduler();
}
class CustomTaskScheduler extends ThreadPoolTaskScheduler {
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period) {
ScheduledFuture<?> future = super.scheduleAtFixedRate(task, period);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
scheduledTasks.put(runnable.getTarget(), future);
return future;
}
@Override
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period) {
ScheduledFuture<?> future = super.scheduleAtFixedRate(task, startTime, period);
ScheduledMethodRunnable runnable = (ScheduledMethodRunnable) task;
scheduledTasks.put(runnable.getTarget(), future);
return future;
}
}
When the scheduling for a bean has to be stopped, you can lookup the map to get the corresponding Future
to it and explicitly cancel it.
当必须停止对 bean 的调度时,您可以查找映射以获取与其对应的映射Future
并显式取消它。
回答by Jairton Junior
Some time ago I had this requirement in my project that any component should be able to create a new scheduled task or to stop the scheduler (all tasks). So I did something like this
前段时间我在我的项目中有这样一个要求,即任何组件都应该能够创建新的计划任务或停止计划程序(所有任务)。所以我做了这样的事情
@Configuration
@EnableScheduling
@ComponentScan
@Component
public class CentralScheduler {
private static AnnotationConfigApplicationContext CONTEXT = null;
@Autowired
private ThreadPoolTaskScheduler scheduler;
public static CentralScheduler getInstance() {
if (!isValidBean()) {
CONTEXT = new AnnotationConfigApplicationContext(CentralScheduler.class);
}
return CONTEXT.getBean(CentralScheduler.class);
}
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
return new ThreadPoolTaskScheduler();
}
public void start(Runnable task, String scheduleExpression) throws Exception {
scheduler.schedule(task, new CronTrigger(scheduleExpression));
}
public void start(Runnable task, Long delay) throws Exception {
scheduler.scheduleWithFixedDelay(task, delay);
}
public void stopAll() {
scheduler.shutdown();
CONTEXT.close();
}
private static boolean isValidBean() {
if (CONTEXT == null || !CONTEXT.isActive()) {
return false;
}
try {
CONTEXT.getBean(CentralScheduler.class);
} catch (NoSuchBeanDefinitionException ex) {
return false;
}
return true;
}
}
So I can do things like
所以我可以做这样的事情
Runnable task = new MyTask();
CentralScheduler.getInstance().start(task, 30_000L);
CentralScheduler.getInstance().stopAll();
Have in mind that, for some reasons, I did it without having to worry about concurrency. There should be some synchronization otherwise.
请记住,出于某些原因,我这样做时不必担心并发性。否则应该有一些同步。
回答by so-random-dude
There is a bit of ambiguity in this question
这个问题有点含糊
- When you say "stop this task", did you mean to stop in such a way that it's later recoverable (if yes, programmatically, using a condition which arises with in the same app? or external condition?)
- Are you running any other tasks in the same context? (Possibility of shutting down the entire app rather than a task) -- You can make use of actuator.shutdown endpoint in this scenario
- 当您说“停止此任务”时,您的意思是要以以后可恢复的方式停止(如果是,以编程方式,使用在同一应用程序中出现的条件?或外部条件?)
- 您是否在同一上下文中运行任何其他任务?(关闭整个应用程序而不是任务的可能性)--您可以在这种情况下使用actuator.shutdown 端点
My best guess is, you are looking to shutdown a task using a condition that may arise with in the same app, in a recoverable fashion. I will try to answer based on this assumption.
我最好的猜测是,您希望以可恢复的方式使用同一应用程序中可能出现的条件关闭任务。我将尝试根据这个假设来回答。
This is the simplest possiblesolution that I can think of, However I will make some improvements like early return rather than nested ifs
这是我能想到的最简单的解决方案,但是我会做一些改进,比如提前返回而不是嵌套 ifs
@Component
public class SomeScheduledJob implements Job {
private static final Logger LOGGER = LoggerFactory.getLogger(SomeScheduledJob.class);
@Value("${jobs.mediafiles.imagesPurgeJob.enable}")
private boolean imagesPurgeJobEnable;
@Override
@Scheduled(cron = "${jobs.mediafiles.imagesPurgeJob.schedule}")
public void execute() {
if(!imagesPurgeJobEnable){
return;
}
Do your conditional job here...
}
Properties for the above code
上述代码的属性
jobs.mediafiles.imagesPurgeJob.enable=true or false
jobs.mediafiles.imagesPurgeJob.schedule=0 0 0/12 * * ?
回答by Tony
Scheduled
预定
When spring process Scheduled
, it will iterate each method annotated this annotation and organize tasks by beans as the following source shows:
spring process 时Scheduled
,它会迭代每个注解这个注解的方法,并按 bean 组织任务,如下源所示:
private final Map<Object, Set<ScheduledTask>> scheduledTasks =
new IdentityHashMap<Object, Set<ScheduledTask>>(16);
Cancel
取消
If you just want to cancel the a repeated scheduled task, you can just do like following (here is a runnable demoin my repo):
如果您只想取消重复的计划任务,您可以执行以下操作(这是我的 repo 中的可运行演示):
@Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor;
@Autowired
private TestSchedule testSchedule;
public void later() {
postProcessor.postProcessBeforeDestruction(test, "testSchedule");
}
Notice
注意
It will find this beans's ScheduledTask
and cancel it one by one. What should be noticed is it will also stopping the current running method (as postProcessBeforeDestruction
source shows).
它会找到这个 beanScheduledTask
并一一取消它。应该注意的是它还会停止当前运行的方法(如postProcessBeforeDestruction
源所示)。
synchronized (this.scheduledTasks) {
tasks = this.scheduledTasks.remove(bean); // remove from future running
}
if (tasks != null) {
for (ScheduledTask task : tasks) {
task.cancel(); // cancel current running method
}
}
回答by TinyOS
Here is an example where we can stop , start , and list also all the scheduled running tasks:
这是一个示例,我们可以在其中停止、启动和列出所有计划运行的任务:
@RestController
@RequestMapping("/test")
public class TestController {
private static final String SCHEDULED_TASKS = "scheduledTasks";
@Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor;
@Autowired
private ScheduledTasks scheduledTasks;
@Autowired
private ObjectMapper objectMapper;
@GetMapping(value = "/stopScheduler")
public String stopSchedule(){
postProcessor.postProcessBeforeDestruction(scheduledTasks, SCHEDULED_TASKS);
return "OK";
}
@GetMapping(value = "/startScheduler")
public String startSchedule(){
postProcessor.postProcessAfterInitialization(scheduledTasks, SCHEDULED_TASKS);
return "OK";
}
@GetMapping(value = "/listScheduler")
public String listSchedules() throws JsonProcessingException{
Set<ScheduledTask> setTasks = postProcessor.getScheduledTasks();
if(!setTasks.isEmpty()){
return objectMapper.writeValueAsString(setTasks);
}else{
return "No running tasks !";
}
}
}
回答by Bin
Define a custom annotation like below.
定义一个自定义注释,如下所示。
@Documented
@Retention (RUNTIME)
@Target(ElementType.TYPE)
public @interface ScheduledSwitch {
// do nothing
}
Define a class implements org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.
定义一个类实现 org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor。
public class ScheduledAnnotationBeanPostProcessorCustom
extends ScheduledAnnotationBeanPostProcessor {
@Value(value = "${prevent.scheduled.tasks:false}")
private boolean preventScheduledTasks;
private Map<Object, String> beans = new HashMap<>();
private final ReentrantLock lock = new ReentrantLock(true);
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
ScheduledSwitch switch = AopProxyUtils.ultimateTargetClass(bean)
.getAnnotation(ScheduledSwitch.class);
if (null != switch) {
beans.put(bean, beanName);
if (preventScheduledTasks) {
return bean;
}
}
return super.postProcessAfterInitialization(bean, beanName);
}
public void stop() {
lock.lock();
try {
for (Map.Entry<Object, String> entry : beans.entrySet()) {
postProcessBeforeDestruction(entry.getKey(), entry.getValue());
}
} finally {
lock.unlock();
}
}
public void start() {
lock.lock();
try {
for (Map.Entry<Object, String> entry : beans.entrySet()) {
if (!requiresDestruction(entry.getKey())) {
super.postProcessAfterInitialization(
entry.getKey(), entry.getValue());
}
}
} finally {
lock.unlock();
}
}
}
Replace ScheduledAnnotationBeanPostProcessor bean by the custom bean in configuration.
用配置中的自定义 bean 替换 ScheduledAnnotationBeanPostProcessor bean。
@Configuration
public class ScheduledConfig {
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationBeanPostProcessor() {
return new ScheduledAnnotationBeanPostProcessorCustom();
}
}
Add @ScheduledSwitch annotation to the beans that you want to prevent or stop @Scheduled tasks.
将 @ScheduledSwitch 注释添加到要阻止或停止 @Scheduled 任务的 bean。
回答by Dário
A working example implementation of @Mahesh 's Option 1, using ScheduledAnnotationBeanPostProcessor.postProcessBeforeDestruction(bean, beanName)
.
@Mahesh 选项 1 的工作示例实现,使用ScheduledAnnotationBeanPostProcessor.postProcessBeforeDestruction(bean, beanName)
.
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;
public class ScheduledTaskExample implements ApplicationContextAware, BeanNameAware
{
private ApplicationContext applicationContext;
private String beanName;
@Scheduled(fixedDelay = 1000)
public void someTask()
{
/* Do stuff */
if (stopScheduledTaskCondition)
{
stopScheduledTask();
}
}
private void stopScheduledTask()
{
ScheduledAnnotationBeanPostProcessor bean = applicationContext.getBean(ScheduledAnnotationBeanPostProcessor.class);
bean.postProcessBeforeDestruction(this, beanName);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
{
this.applicationContext = applicationContext;
}
@Override
public void setBeanName(String beanName)
{
this.beanName = beanName;
}
}