2017-10-17 15 views
1

J'ai une application Spring Boot qui utilise CommandLineRunner et l'annotation Spring @Async pour exécuter une méthode de manière asynchrone. Tout fonctionne bien, mais lorsque tous mes threads sont terminés, l'application se bloque au lieu de quitter.L'application Spring Command Line se bloque après la fin des appels de méthode asynchrone

Voici un exemple minimal de ce que j'ai dans ma demande:

Application.java:

@SpringBootApplication 
@EnableAsync 
public class Application { 
    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 
} 

ApplicationStartup.java:

@Component 
public class ApplicationStartup implements CommandLineRunner { 

    private final AsyncService asyncService; 

    @Inject 
    public ApplicationStartup(final AsyncService asyncService) { 
     this.asyncService = asyncService; 
    } 

    @Override 
    public void run(final String... strings) throws Exception { 
     //my logic is more complicated than this, but this illustrates my point 
     for (int i = 0; i < 1000; i++) { 
      asyncService.runAsyncMethod(); 
     } 
    } 
} 

AsyncService. Java:

@Service 
@Transactional 
public class AsyncService { 

    @Async 
    public void runAsyncMethod() { 
     //perform call to an API and process results 
    } 

} 

ExecutorConfig.java:

@Configuration 
public class ExecutorConfig() { 
    @Bean 
    public ThreadPoolTaskExecutor asyncExecutor() { 
     final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
     executor.setCorePoolSize(64); 
     executor.setMaxPoolSize(64); 
     executor.setQueueCapacity(500); 
     executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); 
     executor.setThreadNamePrefix("Scrub-"); 
     executor.setKeepAliveSeconds(60); 
     executor.initialize(); 
     return executor; 
    } 
} 

Tous mes fils font l'appel à runAsyncMethod() et chaque appel de méthode se termine avec succès, mais l'application est pendante.

J'ai essayé de modifier certains des paramètres de l'exécuteur. Au début, je n'avais pas le keepAliveSeconds, alors j'ai pensé que l'ajouter permettrait de le réparer, mais il resterait accroché après que tous les threads aient été terminés. J'ai changé corePoolSize à 0, et cela a fait sortir l'application quand elle a été faite, mais elle n'a utilisé que 1 thread tout le temps.

Des idées sur la raison pour laquelle l'application ne quitte pas avec la configuration ci-dessus?

+1

En fait, je suspecte que cmdrunner # run se termine * avant que * les méthodes async soient complétées ... pensez à rejoindre vos threads dans la méthode run! (exemple: https://github.com/spring-guides/gs-async-method/blob/master/complete/src/main/java/hello/AppRunner.java) – xerx593

+1

A dû faire myCollection.toArray (new CompletableFuture [myCollection.size()]) '. –

+0

Mais après avoir fait le 'join()' il revient à mon thread principal et exécute l'instruction 'logger.debug()' que j'ai là, mais se bloque toujours dans le fil principal ... –

Répondre

1

Vous avez manqué à rejoindre les emplois asynchrones, c'est pourquoi la sortie de la méthode run (loin) avant toutes les discussions complètes - et le comportement maladroit est « plus compréhensible ».

Selon doc, vous pourriez rejoindre comme:

... 
CompletableFuture<Void>[] myJobs = new CompletableFuture<>[N]; 
... 
for (int i = 0; i < N; i++) { 
     myJobs[i] = asyncService.runAsyncMethod(); 
} 
... 
CompletableFuture.allOf(myJobs).join(); 

Et votre runAsyncMethod() aurait besoin de retourner un CompletableFuture<Void>. Pour ce faire, vous pouvez simplement return CompletableFuture.completedFuture(null);