web-dev-qa-db-ja.com

@Scheduledアノテーションを使用して開始されたスケジュール済みタスクを停止する方法は?

Spring Frameworkの @Scheduled アノテーションを使用して、簡単なスケジュールタスクを作成しました。

 @Scheduled(fixedRate = 2000)
 public void doSomething() {}

ここで、不要になったときにこのタスクを停止します。

このメソッドの開始時に1つの条件付きフラグをチェックする1つの代替方法があることを知っていますが、これはこのメソッドの実行を停止しません。

@Scheduledタスクを停止するためにSpringが提供するものはありますか?

30
Sach141

オプション1:ポストプロセッサの使用

ScheduledAnnotationBeanPostProcessor を指定し、スケジューリングを停止する必要のあるBeanに対して postProcessBeforeDestruction(Object bean, String beanName) を明示的に呼び出します。


オプション2:ターゲットBeanのマップを未来に維持する

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;
    }
}

Beanのスケジューリングを停止する必要がある場合、マップを検索して対応する Future を取得し、明示的にキャンセルできます。

15
Mahesh

この質問には少しあいまいさがあります

  1. 「このタスクを停止する」と言うとき、後で回復できるように停止するつもりでした(もしそうであれば、プログラムで、同じアプリで発生する条件を使用しますか?
  2. 同じコンテキストで他のタスクを実行していますか? (タスクではなくアプリ全体をシャットダウンする可能性)-このシナリオでは、actuator.shutdownエンドポイントを使用できます

私の最善の推測は、同じアプリで発生する可能性のある条件を使用して、回復可能な方法でタスクをシャットダウンしようとしていることです。この仮定に基づいて答えようとします。

これは、私が考えることができる 可能な限り 解決策ですが、ネストされた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...
   }

上記のコードのプロパティ

jobs.mediafiles.imagesPurgeJob.enable=true or false
jobs.mediafiles.imagesPurgeJob.schedule=0 0 0/12 * * ?
6
so-random-dude

しばらく前、プロジェクトでこの要件がありました。どのコンポーネントでも新しいスケジュールされたタスクを作成したり、スケジューラー(すべてのタスク)を停止できる必要があります。だから私はこのようなことをしました

@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;
    }
}

だから私は次のようなことができます

Runnable task = new MyTask();
CentralScheduler.getInstance().start(task, 30_000L);
CentralScheduler.getInstance().stopAll();

いくつかの理由で、並行性を心配することなくそれを行ったことに留意してください。それ以外の場合は、何らかの同期が必要です。

5
Jairton Junior

以下は、すべてのスケジュールされた実行中のタスクを停止、開始、および一覧表示できる例です。

@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 !";
    }
}

}

5
TinyOS

予定

SpringがScheduledを処理するとき、次のソースが示すように、このアノテーションが注釈された各メソッドを反復し、Beanによってタスクを整理します。

private final Map<Object, Set<ScheduledTask>> scheduledTasks =
        new IdentityHashMap<Object, Set<ScheduledTask>>(16);

キャンセル

繰り返しスケジュールされたタスクをキャンセルしたい場合は、次のようにすることができます(ここに私のレポの runnable demo があります):

@Autowired
private ScheduledAnnotationBeanPostProcessor postProcessor;
@Autowired
private TestSchedule testSchedule;

public void later() {
    postProcessor.postProcessBeforeDestruction(test, "testSchedule");
}

通知

このBeanのScheduledTaskを見つけて、1つずつキャンセルします。注目すべきは、現在の実行中のメソッドも停止することです(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
        }
    }
0
Tony