Concurrence et cohérence

Objectifs

Préparation

Cette partie présente quelques constructions et outils disponibles sur la plateforme Java, en se limitant aux éléments nécessaires à la réalisation des exercices.

Les activités (threads) Java

La brève présentation qui suit peut être illustrée et par la lecture de la partie correspondante du cours sur les processus légers (6ème partie, planches 10-13). Les planches suivantes et la documentation présentent plus de détails, leur lecture n'est pas nécessaire dans l'immédiat.

Les planches 10 et 11 fournissent des exemples simples du schéma standard de création de threads

Le modèle de cohérence Java

Par défaut, Java n'offre pas de garantie de cohérence pour les variables partagées, utilisées simultanément par un ensemble de threads. En particulier, les instructions sont susceptibles d'être réordonnées, et la cohérence des caches n'est pas assurée.

Cependant Java offre quelques outils de base pour faciliter la programmation multiactivités avec des variables partagées :

Les verrous Java

Historiquement, il s'agit du premier outil de synchronisation proposé en Java, disponible dès les premières versions du langage. Le service rendu, très élémentaire, s'avère pratique et adapté pour exprimer l'exclusion mutuelle, ce qui fait qu'il reste largement utilisé, même actuellement, car la simple exclusion mutuelle représente le schéma de synchronisation le plus souvent rencontré. En revanche, il se révèle lourd et malcommode dès qu'il s'agit de réaliser des schémas un peu plus évolués que l'exclusion mutuelle. D'où les objets de synchronisation plus classiques et robustes apparus à partir de la version 5 de Java.

La rapide présentation qui suit porte essentiellement sur la syntaxe. Elle peut être complétée par la lecture de la partie correspondante du cours sur les processus légers (planches 36-40) pour les notions et sur la documentation Java en ligne pour les détails techniques.

Principe

Tout objet Java peut être utilisé comme un verrou pour contrôler l'accès à une section critique.

Syntaxe

Autres classes, méthodes et remarques utiles

Exercices

Efficacité de la parallélisation

Il s'agit d'évaluer le gain de temps apporté par la décomposition d'un traitement en plusieurs threads. On considère le traitement réalisé par la classe IncrMes fournie dans l'archive, qui consiste en une boucle qui incrémente un compteur global. Le nombre important d'itérations est destiné à permettre des mesures significatives, mais va nécessiter l'emploi d'entiers longs.

Comparer le temps d'exécution de l'exécution séquentielle de la boucle (lignes 93-107 du code fourni) avec le temps d'exécution d'une application où N threads effectuent chacun 1/N ème de l'itération, N étant un paramètre fourni au lancement de l'application (lignes 114-117 du code fourni).

Coût de la cohérence

Vérifier la correction des résultats obtenus par l'application précédente. Pour cela, chaque thread affichera la valeur du compteur avant de démarrer sa (fraction de) boucle, ainsi que la valeur du compteur après. (Il suffit pour cela de décommenter les affichages placés dans la méthode run(), à la fin du texte du programme.)

Supplément

L'archive fournie contient une classe PCA, qui propose un exercice supplémentaire, dans un but de variété. Cet exercice porte sur les mêmes notions que les exercices autour de la classe IncrMes, et n'introduit rien de nouveau.

Tester les performances d'applications concurrentes en Java : quelques remarques pratiques