IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Introduction à l'API Java 8 Concurrency
Application à la classe ExecutorService, ~un tutoriel de Georges Kemayo

Le , par Georges KEMAYO

59PARTAGES

11  0 
Bonjour,

L'article a pour but de présenter la bibliothèque java.util.concurrent et plus précisément l'API ExecutorService. Il met l'accent sur l'illustration de cette API avec les concepts Java 8.

https://gkemayo.developpez.com/tutor...8-concurrency/

Qu'en pensez-vous?

Retrouver les meilleurs cours et tutoriels pour apprendre la programmation en Java

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de bolojul
Candidat au Club https://www.developpez.com
Le 01/11/2019 à 14:53
Bonjour MisterKool,

Oui il me semble qu'il y a quelques erreurs plus ou moins importantes. A ta dispo pour en discuter. Voici ce que j'ai pu relever :

IV-A

Tu indiques : Nous insistons sur le terme publique, car avec Java 8, on peut désormais retrouver des méthodes privées dans une interface.

En java 8, les interfaces peuvent avoir des méthodes public, static, et default. Les méthodes privées viennent à partir de java 9.

V-A-2. Méthode submit()

Ici il y a une mauvaise utilisation de la méthode awaitTermination :

Code : Sélectionner tout
1
2
3
4
counterThreadService.awaitTermination(3, TimeUnit.SECONDS);
if (sommeProduits.isDone()) {
	System.out.println("Nombre total de produits dans le magasin : " + sommeProduits.get());
}

En effet la documentation précise bien : Blocks until all tasks have completed execution after a shutdown request, or the timeout occur
Or ici aucun shutdown request n'est effectué ! (il est fait après)

La conséquence, c'est qu'il va attendre 3 secondes, même si la tâche se termine bien avant. En fait c'est exactement la même chose que si tu avais mis : sleep(3000).
Pour ne pas attendre 3 secondes, mais au maximum 3 secondes (c'est à dire dès que la tâche est terminée si celle ci se termine avant 3 secondes), il faut :
- soit faire un shutdown avant le awaitTermination:
- soit remplacer les 4 lignes ci dessus par un simple sommeProduits.get(3, TimeUnit.SECONDS) : On va bloquer au maximum 3 secondes. Si la tâche se termine avant, on aura le résultat. Si la tâche se termine après, il faut catcher un TimeoutException

V-B-2. Méthode invokeAny()

L'exemple et le use case utilisé ici n'est pas du tout adapté à l'utilisation de invokeAny, ce qui fait que le résultat produit pourra être faux.
Tu utilises invokeAny pour rechercher en parallèle une valeur dans deux ensembles. Tu supposes alors que le premier qui a trouvé la valeur, retournera son résultat.
Or ce n'est pas le premier qui trouve la valeur qui retourne, mais le premier qui finit sa recherche !! Et ça change tout. Si dans l'ensemble 1 / thread 1, tu as la valeur 1500 recherchée. Et dans l'ensemble 2 / thread 2 tu n'as pas cette valeur. Et que thread 2 termine sa recherche avant thread 1, tu auras alors le message "Aucun thread n a trouvé le CA 1500 recherché". Alors que cette valeur existe dans l'ensemble 1 !

Le cas est très facile à reproduire. Tu crées une liste de 500 valeurs non pas random, mais fixes, un ensemble contenant la valeur recherchée et l'autre non. Tu lances plusieurs fois ton programme, et tu verras que le résultat diffère alors que la liste est fixe, et donc que le résultat devrait être toujours le même.

Pour corriger, il suffit pour les thread ne trouvant pas la valeur, de lancer une exception, et non de renvoyer le message "Aucun thread n a trouvé le CA 1500 recherché". La documentation précisant :
Executes the given tasks, returning the result of one that has completed successfully (i.e., without throwing an exception)
- Si une tâche se termine sans erreurs, c'est qu'elle a trouvé la valeur
- Si aucune tâche ne trouve la valeur, invokeAny lancera une ExecutionException

V-D-2. Méthode scheduleWithFixedDelay()

Même remarque pour le awaitTermination qui ne sert pas à grand chose si shutdown n'a pas été appelé. Un simple sleep permet d'attendre le temps voulu.

V-D-3. Méthode scheduleAtFixedRate()

Tu indiques : que si l'on defini une période de trois minutes par exemple, que la durée d'exécution d'une tâche dure quatre minutes, alors toutes les 3 minutes un nouveau thread sera créé, quitte à avoir plusieurs threads en exécution parallèles

C'est incorrect, aucune thread n'est crée pour compenser une tâche qui serait plus longue que le rate indiqué. Si le cas arrive, la tâche suivante se lance dès que la tâche précédente est terminée.
Tu peux le lire dans la documentation de scheduleAtFixedRate :
If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute

Et de manière générale (c'est un détail !), pour faire des opérations sur les streams contenant des types primitifs (int et long), tu utilises reduce pour faire des opérations simples comme min et sum, alors qu'il est plus efficace (normalement moins d'autoboxing) et plus lisible d'utiliser les méthodes sum() et min() sur les streams primitifs IntStream et LongStream, obtenu par les méthodes mapToInt et mapToLong sur les stream<Integer> et stream<Long>.

En espérant avoir pu aider

Cordialement
1  0 
Avatar de Georges KEMAYO
Membre chevronné https://www.developpez.com
Le 03/11/2019 à 13:38
Bonjour Bolojul,

J'étais sceptique au départ, mais force est de constater que tes remarques sont pertinentes.

J'ai pris en compte tes remarques et répercuté cela dans l'article.

Pour ta remarque sur Java 8 et sur les méthodes privées, j'ai dû effectivement me mêler les painceaux avec Java 9. Je prise que j'ai écrit l'article à une date où Java 9 était déjà sortie.

Pour ta remarque sur awaitermination de la section V-D-2, c'est fait exprès car l'idée est de voir le thread écrire ses résultats. On aurait pu utiliser sleep, mais c'est un point de vue.

Enfin, pour la gestion des Stream et ses fonctions, tu as raison, mais mon but n'est pas d'optimiser le code. J'ai expressément utilisé les Wrapper des types primitifs (int, long) pour utiliser les fonctions un peu avancées des Streams (comme reduce) afin de vulgariser ces dernières, c'est un choix. Si tu regardes d'ailleurs les différents blocs de code, tu verras qu'il y a des instructions que j'aurai pu compacter et optimiser. Mais je les ai laissé detailler par souci de pédagogie et clarté pour le lecteur.

Pour le reste tu as totalement raison et je t'en remercie pour ton investissement et tes remarques.

Cordialement.
1  0 
Avatar de bolojul
Candidat au Club https://www.developpez.com
Le 09/08/2019 à 11:32
Bonjour,

Merci pour cet article.
Cependant j'aimerais remonter quelques erreurs, et faire quelques remarques, c'est possible ?

Cordialement
0  0 
Avatar de Georges KEMAYO
Membre chevronné https://www.developpez.com
Le 01/11/2019 à 0:27
Bonjour Bolojul,

Je suis bien curieux de lire ces erreurs De quel type d'erreurs parles-tu ?
Je reste à l'écoute.

Cordialement,
0  0