Home » Java » java – ScheduledExecutorService inside a Spring Bean, not working after a couple of executions-Exceptionshub

java – ScheduledExecutorService inside a Spring Bean, not working after a couple of executions-Exceptionshub

Posted by: admin February 25, 2020 Leave a comment

Questions:

I am trying to schedule a task inside a Spring @Bean which will update the property of the instance returning from Bean.

I am able to run this code, and the executor works fine a couple of times, but after that, it suddenly stops loading.

What exactly is the problem here? Is there a better way to work this out??

@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;
}
How to&Answers:

The lifecycle of serviceLoader looks weird – it gets initialized right during the method or service, then schedules some work and then service gets returned. What happens to the reference to this pool? when the shutdown can be called?

In addition, the first iteration runs immediately, and this happens when the application context is not ready yet, this can lead to unpredictable results depending on the actual code that runs during the iteration.

I can’t say for sure what happens based on this code snippet, but here are some possible solutions:

  1. Use @Scheduled annotation, running scheduled tasks is a bulit-in spring feature. There are a lot of tutorials, here is one of them

  2. If you absolutely have to use the thread pool, I suggest the following configuration:

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

With this approach you can be sure that the service will start refreshing when the application context is ready.

The lifecycle of the executor service is well-defined as well, spring manages it as a regular singleton bean, so it won’t be GC’ed as long as the application context is up-and-running.

The presence of destroy method guarantees graceful shutdown (again, spring will call it for you).