J'essaie d'entrer dans le multithreading du printemps et j'ai quelques questions.
J'ai une méthode exécutable dans la classe ThreadRating. Maintenant, je ne suis pas sûr de la meilleure façon de l'utiliser.
option 1 j'ai trouvé:
private void updateRating() {
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 10; i++) { // test
// thread part
Runnable worker = new ThreadRating(path, i, products.get(i), dao, fileHandler);
executor.execute(worker);
}
executor.shutdown();
try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
log.error("There was an error when ending threads");
System.exit(1);
}
System.out.println("Finished all threads");
}
Cela semble bien fonctionner. Après la boucle for, il attend la fin des threads et se termine.
Deuxième option que j'ai essayée
private TaskExecutor taskExecutor;
public UpdateBO(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
private void updateRating() {
for (int i = 0; i < 10; i++) { // test
Runnable worker = new ThreadRating(path, i, products.get(i), dao, fileHandler);
taskExecutor.execute(worker);
}
// wait for threads to be finished before you go any further ??
}
Et dans le fichier xml j'ai
<beans:bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<beans:property name="corePoolSize" value="5" />
<beans:property name="maxPoolSize" value="10" />
<beans:property name="queueCapacity" value="25" />
<beans:property name="waitForTasksToCompleteOnShutdown" value="true" />
</beans:bean>
<beans:bean id="updateBO" class="UpdateBO">
<beans:constructor-arg ref="taskExecutor" />
</beans:bean>
Et voici mes questions:
executor.execute(worker);
- juste pour clarifier. Ce n'est pas réellement la création d'un nouveau thread, mais cela ajoute une tâche à une file d'attente et si la file d'attente est pleine, elle attend directement sur cette ligne dans le code jusqu'à ce qu'il y ait de la place libre?Merci de m'avoir aidé à comprendre.
Différence principale: dans l'option 1) vous créez un nouvel exécuteur à chaque appel de updateRating()
, dans l'option 2) l'exécuteur est créé une fois au moment du déploiement, vous alimentez le même exécuteur unique avec de nouvelles tâches. La deuxième approche est bien meilleure.
Pourquoi avez-vous besoin d'arrêter l'exécuteur testamentaire? Créer de nouveaux exécuteurs et les arrêter pour attendre que la tâche soit traitée est anti-modèle . N'oubliez pas que les exécuteurs sont créés afin de contrôler les ressources du système et doivent être traités de la sorte. (Par exemple, vous avez un pool de connexions DB de 50 connexions - afin de servir l'accès DB, vous créez un exécuteur de 50 threads - pour éviter que la limite de connexion ne dépasse. Ou vous avez 24 cœurs sur le serveur et devez paralléliser le travail de la meilleure façon possible) .
Et, comme je l'ai mentionné en commentaire, dans certains environnements (tels que les serveurs d'applications), vous n'avez souvent pas le droit d'arrêter l'exécuteur testamentaire. Une telle tentative produira SecurityException
.
Si vous devez attendre que les employés terminent leur travail, encapsulez chaque travail avec Callable
au lieu de Runnable
, puis à partir du thread principal, appelez future.get()
- et il se bloquera jusqu'à ce que le travail finitions. Les délais d'attente sont pris en charge. Exemple
Absolument raison. Les threads sont créés et détruits par l'exécuteur lui-même, quand il pense que le meilleur moment est de le faire. Essayez de surveiller votre application avec jvisualvm pour voir comment cela se passe.
1.) L'option 1 est mal implémentée car vous définissez votre service exécuteur localement et le fermez après chaque utilisation. Cela va à l'encontre de l'objectif de création d'un pool de threads - il doit être un objet global, donc l'option 2 est la voie à suivre.
2.) Vous n'avez pas besoin d'arrêter le service exécuteur lors de l'appel d'un service Web. Si le webservice ne répond pas, l'appel finira par expirer et le thread remplira l'exécution complète. Si vous arrêtez le service exécuteur testamentaire, il ne sera pas disponible pour le prochain appel.
3.) Si vous avez besoin d'une forme de notification une fois votre thread finalisé, utilisez plutôt Callable en conjonction avec Futures .
4.) Votre service exécuteur dispose d'un maximum de 10 threads alloués, il ne se reproduira pas plus que ceux-ci. Si tous sont occupés, votre tâche sera inactive jusqu'à ce que l'un de ces threads devienne disponible.