Thread

Thread et exécutions concurrentes

Introduction

De nos jours, les ordinateurs sont multi-tâches et sont capables d’exécuter plusieurs opérations simultanément (mise à jour en tâche de fond, contrôle de la bonne exécution d’une opération par une application tierce, etc).

Il est fréquent que plusieurs acteurs accèdent de manière concurrente à une même ressource. Cela peut engendrer différents problèmes.

Observations

Parallélisme

Compiler et exécuter la classe Thread1Parallelisme.

Le programme crée trois threads qui affichent 100 fois respectivement Hello, World et and Everybody.

Que pouvez vous dire à propos de l’ordre d’affichage ? Expliquer.

Remarque

Ce programme hérite de Thread et réimplémente la méthode run() pour obtenir des flots d’exécutions parallèles.

Mise en pause d’un Thread

Compiler et exécuter plusieurs fois la classe Thread2Sleep.

Voyez vous tous les affichages ?

Essayez d’augmenter les valeurs d’attente (le troisième argument passé lors de la création des threads). Par quelle méthode de l’API Java est utilisé cette valeur ?

Si vous mettez de trop grandes valeurs, vous devriez parfois ne plus voir aucun affichage.

Pourquoi ?

Attente de fin d’exécution d’un Thread

Dans la classe Thread3Join, nous faisons appel à la fonction join qui force le programme principal à attendre la terminaison des threads.

Compiler le programme, Modifiez les valeurs d’attente (paramètre delai) et s’assurer que tous les affichages sont présents.

Modification conflictuelle d’une même ressource

Dans la classe Thread4ModificationConflictuelle, nous manipulons une variable partagée d etype Tableau.

Les Thread sont instanciés en leur donnant en paramètre un objet de type Runnable : la méthode Runnable.run() est exécutée par le Thread.

Ces objets sont fabriqués par la classe TacheTableau. Deux types de tâches sont effectuées:

Logiquement, à la fin les valeurs doivent être égales à 0 puisque les tâches incrémentant et décrementant s’annulent.

Exécuter plusieurs fois le programme. Y a-t-il des cas où le résultat est différent ?

Exclusion mutuelle avec synchronized

Pour corriger le problème dans l’exemple précédent, on ajoute le mot clé synchronized aux deux méthodes incrementerValeurs et decrementerValeurs.

Exécuter le programme. S’assurer que le comportement est correct. Que remarquez vous au niveau du temps d’exécution ? Expliquer.

Compte en banque non sécurisé

Dans Thread6CompteEnBanqueSansVerrou la ressource partagée est un compte et non un tableau. Les opérations sont débiter, créditer et consulter.

Est-ce que le solde est toujours égal à 0 à la fin ?

Manipulation de comptes mutuellement exclusives

Dans la classe Thread7CompteSynchronized, nous reprenons l’exemple de manipulation de comptes.

Le programme est modifié pour interdire le retrait s’il n’y a pas assez d’argent.

Quand le solde est insuffisant, les threads de retrait attendent (wait() dans la méthode debiter), ils sont réveillés par les threads de dépôt qui appellent la méthode crediter contenant un appel à notify().

Compiler et observer l’exécution de Thread7CompteSynchronized.

Essayer de trouver un cas où un appel à retirer est fait avec un solde 0. Que se passe-t-il ?

Manipulation de comptes mutuellement exclusives version sans boucle

La classe Thread8MutexSansBoucle utilise CompteMutexIf est quasi identique à la classe CompteMutex.

La différence tient dans le fait que pour la vérification de la condition avant wait, nous avons remplace le while par un if.

Y a-t-il des cas où le solde devient négatif ? Expliquer.

Exercices

Semaphore et Lock

Cet exercice est à faire initialement sur feuille papier, avant d’être programmé.

Faites une recherche sur le concept de sémaphore dans le domaine du parallélisme.

Etudiez la classe DemoSemaphore. Modifez les valeurs suivantes dans le main:

Observez ce qui se passe et concluez.

Faites de même avec la classe DemoLock.

Etudiez la documentation sur la classe Semaphore.

Etudiez cette classe et l’interface Lock (et les classes qui l’implémentent).

Le problème du producteur/consommateur (verrou)

Programmer le problème du producteur/consommateur en utilisant les Lock. Pour cela, écrire une classe Stockage contenant un tableau d’Object ainsi que les méthodes Object consomme() et void produit(Object).

Écrire un programme principal qui instancie un objet Stockage puis qui crée de nombreux threads dont certains vont produire et d’autres consommer des objets.

Faites en sorte que l’on puisse observer le déroulement des synchronisations (par exemple avec des System.out.println() judicieusement placés). Pour cela, vous aurez probablement à ralentir vos threads (en insérant des appels à sleep(int) à l’intérieur des sections critiques ou entre les productions/consommations d’objets).

Note : vous pouvez utiliser la classe Random (voir la Javadoc) pour générer des temps d’attente aléatoires, ainsi que la classe TimeUnit.

Gardez des traces montrant que certains threads sont effectivement endormis (et réveillés plus tard) lorsqu’ils essayent de consommer alors que le stockage est vide ou bien au contraire lorsqu’ils essayent de produire alors que le stockage est plein.

Le problème du producteur/consommateur (moniteurs)

Réécrivez le problème du producteur/consommateur en utilisant directement les moniteurs Java (i.e. sans utiliser la classe Semaphore ou Lock mais uniquement synchronized, wait et notify).

Application des CountDownLatch et CyclicBarrier

Ecrivez un programme simulant un repas fait par quatre convives (par l’affichage de messages).

Les quatres convives doivent attendre d’être attablés avant de commencer le repas.

Une fois le repas commencé, il se déroule comme suit:

Un serveur (géré par un Thread) enlève puis ramène les assiettes 2 par 2. Il met 1 minute pour effectuer un aller-retour en cuisine.

Simulez ce repas (en remplaçant les minutes par des secondes pour plus de rapidité).

Le programme doit inscrire dans un fichier les temps totaux d’exécution pour chaque repas (donc chaque lancement du programme).

Ressources

Un exemple d’utilisation de CyclicBarrier:

http://java67.blogspot.fr/2015/06/how-to-use-cyclicbarrier-in-java.html

Une ressource pour les deux:

http://oppansource.com/cyclicbarrier-and-countdownlatch-in-java/

Un algorithme de tri parallèle:

http://www.javamex.com/tutorials/threads/CyclicBarrier_parallel_sort.shtml

Quelques explications sur l’usage de CountDownLatch:

http://howtodoinjava.com/2013/07/18/when-to-use-countdownlatch-java-concurrency-example-tutorial/