Interaction entre threads
Maintenant qu'on a vu comment arrêter proprement un thread, on va s'intéresser à un autre problème de contrôle, à savoir cela de gérer l'interaction entre threads. Pour ce faire, il existe un mécanisme dans Java qu'on va étudier dans cette section. Ce mécanisme est à la base des nombreuses formes d'interaction courantes en programmation concurrente et il est donc très intéressant de bien le comprendre.
Tout ce qu'on va voir ici se base sur des méthodes de la classe Object :
public final void wait() throws InterruptedException; public final void notify(); public final void notifyAll();
Pour qu'un thread puisse appeler une de ces méthodes sur un objet, il faut que le thread possède le lock de l'objet. L'appel doit donc se faire dans un bloc synchronized sur l'objet, comme on l'a vu au chapitre 2. Sinon, une exception de type IllegalMonitorStateException sera levée (exception hors-contrôle).
Endormir un thread
Tous les objets possèdent, outre un lock, un ensemble de threads en attente. Pour un objet o, lorsqu'on exécute la méthode o.wait(), le thread qui fait l'appel passe dans l'état Waiting. De plus, le lock de o que le thread possédait est libéré.
Voyons cela sur l'exemple suivant. La première chose qui est faite est d'obtenir le lock de l'objet courant. Ensuite, on appelle la méthode wait sur ce même objet. À ce moment, le thread qui exécute cette tâche est endormi (passe dans l'état Waiting) et ajouté à la liste d'attente de l'objet courant. Tant qu'il n'est pas réveillé, il ne sera donc plus exécuté sur la machine. De plus, une fois ajouté à la liste d'attente, sachez que le thread libère le lock qu'il possédait.
|
|
Voyons un exemple d'utilisation de cette classe. On va créer un nouveau thread qui exécute la tâche représentée par la classe SleepingTask. Ce programme ne se terminera pas. En effet, bien que le thread principal se termine, le thread exécutant SleepingTask est toujours vivant, ce qui empêche le programme de se terminer.
|
|
La figure suivante montre la situation finale, lorsque le programme est bloqué. On voit bien qu'il reste un thread actif, mais qui est endormi. Aucun autre thread n'est disponible pour le réveiller et le programme restera donc bloqué indéfiniment.

Réveiller un thread
Pour réveiller un thread qui est en endormi et dans la file d'attente d'un objet o, il suffit d'exécuter la méthode o.notify(). Le thread qui appelle cette méthode doit avant tout posséder le lock sur l'objet. Une fois cette méthode appelée, un des threads de la file d'attente va se faire réveiller. Néanmoins, ce dernier ne pourra pas se remettre à s'exécuter directement, il devra avant tout ré-obtenir le lock. En effet, l'appel wait est fait dans un bloc synchronized et il faut donc le lock pour pouvoir en sortir.
Voyons maintenant comme débloquer le programme précédent, en ajoutant une instruction de réveil dans le thread principal.
|
|
Une fois le thread exécutant SleepingTask démarré, on met le thread principal en attente pendant cinq secondes. Ensuite, on appelle la méthode task.notify() afin de réveiller un thread se trouvant dans la file de cet objet. Ceci va débloquer le programme et on pourra voir sur la sortie standard :
Go for sleeping Main thread ended Normal End
Notez que l'ordre d'apparition des lignes pourrait être différent. Les deux dernières lignes pourraient être inversées. Vous voyez pourquoi ?
Réveiller tous les threads
La méthode notify permet donc de réveiller un des threads qui est en attente. On peut également décider de réveiller tous les threads qui sont en attente en utilisant la méthode notifyAll.





















