Communication entre Threads en Java

La communication entre threads permet aux unités d'exécution concurrentes de s'envoyer des signaux et de synchroniser leur activité. Il existe plusieurs mécanismes pour réaliser cela, allant de techniques simples mais inefficaces à des approches plus robustes intégrées au langage.

Transmission via un Objet Partagé

Une méthode initiale consiste à utiliser une variable partagée comme indicateur d'état. Un thread A met à jour un drapeau dans un bloc synchronisé, et un thread B vérifie ce drapeau. La synchronisation garantit la visibilité des modifications.

public synchronized boolean estPret() {
    return pretTraitement;
}

public synchronized void setPret(boolean estPret) {
    this.pretTraitement = estPret;
}

}


<div></div></div>Les deux threads doivent détenir une référence vers la même instance de `EtatDonnees`.

Attente Active (Busy Waiting)
-----------------------------

Le thread consommateur peut tourner en boucle pour vérifier l'état de la variable partagée. Cette approche consomme inutilement du temps CPU, surtout si le délai d'attente est long.

<div><div></div>```
while (!etatPartage.estPret()) {
    // Boucle active, consommant du CPU
}

Le langage Java offre un mécanisme de suspend interne via la classe Object. La méthode wait() place le thread appelant dans un état inactif, libérant le moniteur (le verrou). La méthode notify() réveille un unique thread en attente sur le même moniteur, tandis que notifyAll() les réveille tous. Ces appels doivent impérativement se faire dans un bloc synchronisé sur l'objet concerné.

public void attendre() throws InterruptedException {
    synchronized (verrou) {
        verrou.wait();
    }
}

public void notifier() {
    synchronized (verrou) {
        verrou.notify();
    }
}

}


<div></div></div>Signaux Perdus
--------------

Les méthodes `notify()` et `notifyAll()` ne stockent pas les signaux. Si `notify()` est appelée avant qu'un thread n'exécute `wait()`, le signal est perdu. Pour éviter cela, on utilise un indicateur booléen.

<div><div></div>```
public class SignaleurRobuste {
    private final Object verrou = new Object();
    private boolean signalRecu = false;

    public void attendre() throws InterruptedException {
        synchronized (verrou) {
            while (!signalRecu) {
                verrou.wait();
            }
            signalRecu = false; // Consommer le signal
        }
    }

    public void notifier() {
        synchronized (verrou) {
            signalRecu = true;
            verrou.notify();
        }
    }
}

Un thread peut se réveiller de wait() sans avoir reçu de notification. C'est un phénomène connu. Pour le gérer, la vérification de la condition se fait dans une boucle while plutôt que dans une instruction if. Cela force le thread à reverifier la condition avant de continuer.


<div></div></div>Synchronisation pour Plusieurs Threads
--------------------------------------

Lorsque plusieurs threads sont en attente et que `notifyAll()` est utilisé, la boucle `while` sur la condition permet à chaque thread de reverifier l'état après son réveil. Seul le thread pour lequel la condition est réellement vraie procédera, les autres retourneront en attente.

Objet de Synchronisation Approprié
----------------------------------

Il est crucial d'éviter d'utiliser des objets partagés inappropriés comme cible de `wait()`/`notify()`. Par exemple, utiliser une chaîne de caractères littérale (comme `""`) ou un objet singleton comme moniteur peut mener à des interférences imprévisibles entre des composants non liés. En effet, le compilateur/JVM peut interner les chaînes littérales, les faisant pointer vers une même instance en mémoire. Cela pourrait provoquer qu'un thread d'un module soit réveillé par une notification destinée à un thread d'un autre module, entraînant des signaux perdus ou des réveils erronés.

La pratique recommandée est de dédier un objet explicite (comme l'objet `verrou` dans les exemples ci-dessus) pour chaque mécanisme de communication spécifique. Cet objet ne doit pas être accessible publiquement afin de limiter l'accès aux méthodes `wait()` et `notify()` aux seules opérations de synchronisation intentionnelles.

Étiquettes: Java multithreading thread-communication wait-notify Synchronization

Publié le 11 juin à 18h12