ScheduledExecutorService внутри Spring Bean, не работающий после пары выполнений

Я пытаюсь запланировать задачу внутри Spring @Bean, которая обновит свойство экземпляра, возвращаемого из Bean.

Я могу запустить этот код, и исполнитель пару раз работает нормально, но после этого он внезапно перестает загружаться.

В чем именно проблема? Есть ли лучший способ решить это??

@Bean(name="service")
public Service getService(){
  Service service = new Service();
  ScheduledExecutorService serviceLoader = Executors.newScheduledThreadPool(1);
    serviceLoader.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            service.loadAllLiveEvents();
        }
    }, 0, 1, TimeUnit.HOURS);

  return service;
}

person defcon    schedule 18.02.2020    source источник


Ответы (1)


Жизненный цикл serviceLoader выглядит странно — он инициализируется прямо во время выполнения метода или службы, затем назначает некоторую работу, а затем служба возвращается. Что происходит со ссылкой на этот пул? когда отключение может быть вызвано?

Кроме того, первая итерация запускается сразу, и это происходит, когда контекст приложения еще не готов, это может привести к непредсказуемым результатам в зависимости от фактического кода, который выполняется во время итерации.

Я не могу точно сказать, что происходит на основе этого фрагмента кода, но вот несколько возможных решений:

  1. Используйте аннотацию @Scheduled, запуск запланированных задач — это встроенная функция Spring. Учебников много, вот один из них

  2. Если вам абсолютно необходимо использовать пул потоков, я предлагаю следующую конфигурацию:

@Configuration
public class MyConfiguration {
   @Bean 
   public Service service() {
      return new Service();
   }
   @Bean(destroyMethod="shutdownNow") // or shutdown - now spring will close the pool when the app context gets closed
   @Qualifier("serviceLoaderPool") 
   public ScheduledExecutorService serviceLoader() {
        return Executors.newScheduledThreadPool(1);
   }

   @EventListener
   public void onAppContextStarted(ApplicationReadyEvent evt) {
       ScheduledExecutorService loader = 
       (ScheduledExecutorService)evt.getApplicationContext().getBean("serviceLoaderPool");
       Service service = evt.getApplicationContext.getBean(Service.class);
       loader.scheduleAtFixedRate(new Runnable() {
           @Override
           public void run() {
               service.loadAllLiveEvents();
           }
       }, 0, 1, TimeUnit.HOURS);
   }
}

При таком подходе вы можете быть уверены, что служба начнет обновляться, когда контекст приложения будет готов.

Жизненный цикл службы-исполнителя также четко определен, Spring управляет им как обычным одноэлементным компонентом, поэтому он не будет подвергаться сборке мусора, пока контекст приложения запущен.

Наличие метода destroy гарантирует корректное завершение работы (опять же, Spring вызовет его за вас).

person Mark Bramnik    schedule 19.02.2020