una tick segundero parar método metodo hora especifica ejecutar detener como java spring scheduled-tasks

java - tick - ¿Cómo detener una tarea programada que se inició con la anotación @Scheduled?



segundero java (5)

Opción 1: uso de un postprocesador

Proporcione ScheduledAnnotationBeanPostProcessor e invoque explícitamente postProcessBeforeDestruction(Object bean, String beanName) , para el bean cuya programación debe detenerse.


Opción 2: mantener un mapa de beans objetivo para su futuro

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

Cuando se debe detener la programación de un bean, puede buscar el mapa para obtener el Future correspondiente y cancelarlo explícitamente.

He creado una tarea programada simple usando la anotación @Scheduled Spring Framework.

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

Ahora quiero detener esta tarea, cuando ya no sea necesaria.

Sé que podría haber una alternativa para verificar un indicador condicional al comienzo de este método, pero esto no detendrá la ejecución de este método.

¿Hay algo que Spring proporcione para detener la tarea @Scheduled ?


Programado

Cuando el proceso de primavera está Scheduled , iterará cada método anotado esta anotación y organizará las tareas por beans como muestra la siguiente fuente:

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

Cancelar

Si solo desea cancelar una tarea programada repetida, puede hacer lo siguiente (aquí hay una demostración ejecutable en mi repositorio):

@Autowired private ScheduledAnnotationBeanPostProcessor postProcessor; @Autowired private TestSchedule testSchedule; public void later() { postProcessor.postProcessBeforeDestruction(test, "testSchedule"); }

aviso

Encontrará el ScheduledTask este ScheduledTask y lo cancelará uno por uno. Lo que debe notarse es que también detendrá el método de ejecución actual (como muestra la fuente 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 } }


Aquí hay un ejemplo en el que podemos detener, iniciar y enumerar también todas las tareas de ejecución programadas:

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

}


Hace algún tiempo tuve este requisito en mi proyecto de que cualquier componente debería poder crear una nueva tarea programada o detener el programador (todas las tareas). Entonces hice algo como esto

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

Entonces puedo hacer cosas como

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

Tenga en cuenta que, por alguna razón, lo hice sin tener que preocuparme por la concurrencia. Debería haber alguna sincronización de lo contrario.


Hay un poco de ambigüedad en esta pregunta

  1. Cuando dijo "detener esta tarea", ¿quiso parar de tal manera que luego sea recuperable (si es así, programáticamente, utilizando una condición que surge en la misma aplicación? O condición externa?)
  2. ¿Estás ejecutando otras tareas en el mismo contexto? (Posibilidad de cerrar la aplicación completa en lugar de una tarea): puede utilizar el punto final actuator.shutdown en este escenario

Mi mejor conjetura es que está buscando cerrar una tarea utilizando una condición que puede surgir en la misma aplicación, de manera recuperable. Trataré de responder en base a esta suposición.

Esta es la solución más simple posible en la que puedo pensar. Sin embargo, haré algunas mejoras como el retorno temprano en lugar de anidarse si s

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

Propiedades para el código anterior

jobs.mediafiles.imagesPurgeJob.enable=true or false jobs.mediafiles.imagesPurgeJob.schedule=0 0 0/12 * * ?