4 votes

Appel de Java notify() dans une condition if

Je viens d'écrire un exemple simple en java pour me familiariser avec le concept des méthodes wait et notify.

L'idée est que lorsque l'on appelle notify() le thread principal imprimera la somme.

Classe MyThread

public class MyThread extends Thread {
    public int times = 0;

    @Override
    public void run() {

        synchronized (this) {
            try {
                for (int i = 0; i < 10; i++) {
                    times += 1;
                    Thread.sleep(500);
                    if (i == 5) {
                        this.notify();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

Classe principale

public class Main {

    public static void main(String[] args) {

        MyThread t = new MyThread();
        synchronized (t) {
            t.start();
            try {
                t.wait();
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(t.times);
        }
    }
}

Résultats attendus

5 mais j'ai eu 10 à la place.

Eh bien, ce que je pense, c'est que lorsque notify() est appelé, le thread principal se réveillera et exécutera la fonction System.out.println(t.times) ce qui devrait donner 5. Ensuite, le run() continuera jusqu'à la fin de la boucle for qui mettra à jour la valeur de times à 10.

Toute aide est la bienvenue.

5voto

khwilo Points 1413

Les blocs synchronisés impliquent une exclusion mutuelle. À tout moment, un seul thread est autorisé à détenir le verrou et à exécuter le code dans un bloc synchronisé. Cette règle s'étend à tous les blocs gardés par le même verrou.

Dans votre cas, il y a deux blocs de ce type qui utilisent le même verrou, donc c'est soit le fil principal, soit le fil secondaire. MyThread qui est autorisé à exécuter du code dans l'un de ces blocs, l'autre thread doit attendre. Ainsi, vous avez ici le scénario suivant :

  1. Le fil principal acquiert le verrou.
  2. Le fil principal lance le deuxième fil.
  3. Le second thread atteint le bloc synchronisé mais ne peut pas y entrer car le verrou est maintenu par le thread principal.
  4. Le fil principal appelle wait() . Cet appel libère le verrou et place le fil d'exécution principal dans l'état d'urgence. WAITING l'État.
  5. Le deuxième thread peut maintenant acquérir le verrou et entrer dans le bloc synchronisé.
  6. Le deuxième fil compte jusqu'à cinq et appelle notify() . Cet appel ne libère pas le verrou, il informe simplement le thread principal qu'il peut avancer dès qu'il peut réacquérir le verrou.
  7. Le thread principal se réveille, mais il ne peut pas progresser car il ne peut pas réacquérir le verrou (il est toujours détenu par le second thread). Rappelez-vous, deux threads ne peuvent pas être actifs à la fois dans un bloc synchronisé gardé par le même verrou, et maintenant, le deuxième thread est toujours actif, donc le thread principal doit continuer à attendre.
  8. Le deuxième fil continue à compter, met times à 10 et quitte finalement le bloc synchronisé, libérant le verrou.
  9. Le fil d'exécution principal récupère le verrou et peut à présent progresser vers l'étape suivante. println . Mais à ce moment-là, le times est déjà de 10.

Utilisation de join() ne vous aidera pas non plus car le résultat sera le même - le thread principal ne pourra progresser que lorsque le second sera terminé.

Si vous voulez que votre thread principal continue l'exécution dès que le second thread frappe 5 vous devez acquérir le verrou et le libérer immédiatement après cet événement :

public class MyThread extends Thread {
    public volatile int times = 0;

    @Override
    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                times += 1;
                Thread.sleep(500);
                if (i == 5) {
                    synchronized(this) {
                        this.notify();
                    }
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

N'oubliez pas de faire times volatile, sinon la JVM ne garantira pas que vous verrez sa valeur réelle dans votre thread principal.

Vous devez également comprendre que cette approche ne garantit pas que votre thread principal imprime 5. Il se peut qu'au moment où il atteigne la fonction println le second thread effectue une ou deux itérations, voire plus, et vous verrez quelque chose de plus grand que 5 (bien que ce soit très malencontreux en raison de l'utilisation de l'option sleep() à chaque itération).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X