4

I need a POJO method to execute asynchronously, so I've annotated it with @Async. I've added @EnableAsync to my @Configuration class with the proper @ComponentScan. Here's a small test case for you to run.

public class Test {
    public static void main(String[] args) throws InterruptedException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        context.register(MyConfig.class);
        context.refresh();
        Object o = context.getBean(AsyncBean.class);

        //((AsyncBean)o).doStuff();

        System.out.println(o);

    }

    @ComponentScan(basePackages = "my.package")
    @EnableAsync
    @Configuration
    // @EnableScheduling
    public static class MyConfig {
        @Bean
        public AsyncBean bean() throws InterruptedException {
            AsyncBean b = new AsyncBean();
            return b;
        }
    }

    public static class AsyncBean {
        //@Scheduled(fixedRate = 10000L, initialDelay = 1000L)
        @Async
        public void doStuff() throws InterruptedException {
            for (int i = 0; i < 5; i++) {
                System.out.println("async loop" + i + " -> " + Thread.currentThread().getId());
                Thread.sleep(1000L);
            }
        }
    }
}

The code above will load the AnnotationConfigApplicationContext and quit. If, however, I un-comment //((AsyncBean)o).doStuff();, then that will run in a separate thread. Why is it that the @Async method doesn't get started when the configuration is completely read? That's what I would expect.

I've left some @Scheduled stuff above so you can try it yourself. In the case of @Scheduled, the annotated method gets triggered right away (after initial delay that is).

Is there something else I need to implement for Spring to know it has to start my @Async methods?

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724

1 Answers1

17

@Async is not intended to be run after the loading of the ApplicationContext. It is intended to run the annotated method asynchronously when it is invoked.

If you want a method to run at application startup, then you should use the @PostConstruct annotation (on a non lazily loaded bean). If you need that method to run asynchronously, then you will have to be a bit more tricky, as you use both @PostConstruct and @Async simultaneously (as noted in the last paragraph of 25.5.2 here).

EDIT:

The differences between @Async and @Scheduled may not be the most clear from the documentation. In general, @Scheduled is used to tell when the next invocation of a specific method should take place, and it typically periodic. @Async is used to run a method asynchronously, that is, the method will return immediately after starting up a background thread to do the work of the method.

The confusing part for this is the background thread. They both use one, but the nature of what they are trying to do is different (periodic background work which is generally non user interactive vs one time background work typically initiated by a user).

nicholas.hauschild
  • 42,483
  • 9
  • 127
  • 120
  • Is there any documentation that describes this difference between `@Async` and `@Scheduled` in more detail? – Sotirios Delimanolis May 27 '13 at 23:50
  • I just stumbled upon this answer and I gotta say it really simplified some optimizations I was tinkering with for a project (asynchronously populating a cache without blocking my server startup). Thank you very much! – BSJ Nov 05 '14 at 19:58
  • 1
    An ingenious solution was provided in another discussion thread: use ``@Scheduled(fixedRate = Long.MAX_VALUE`` (see https://stackoverflow.com/a/55818587/3627515) – leonidos79 Feb 20 '20 at 12:41